一、提高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中启用OpenMP
https://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。
#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
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
五、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,如果它们能够持续发展下去)。
参考文献:
六、引申阅读之TaskCpp
https://github.com/qicosmos/TaskCppV1.1
TaskCpp是c++11开发的一个跨平台的并行task库,它的设计思路来源于微软的并行计算库ppl和intel的并行计算库tbb
---
欢迎访问姊妹篇《关于实现Halcon算法加速的基础知识》