最近在做人脸检测的速度优化工作,对OPENCV下利用GPU加速人脸检测做了一些了解,现对齐进行梳理,了解下大概,主要内容来自
OPENCV tutorial on GPU
首先,是关于OPENCV上GPU的一些说明。

1.GPU有自己的内存,当你把Mat类型数据从硬盘读入到系统内存后,为了使用GPU模块,还必须将数据上传到GPU,GPU计算完后,再将计算结果下载下来,上传和下载一般比较耗时,因此不推荐将体量小的函数移植到GPU上使用,那样,GPU并行计算的能力会因长时间的上传下载操作而得不到充分展现。

2.OPENCV在GPU上有类似于Mat的数据结构,GpuMat。GpuMat与Mat的差别是,GpuMat只能包含2D数据且不能返回GpuMat的引用,简单的使用如下:

Mat I1; // Main memory item - read image into with imread for example
gpu::GpuMat gI; // GPU matrix - for now empty
gI1.upload(I1); // Upload a data from the system memory to the GPU memory

I1 = gI1; // Download,gI1.download(I1) will work too

3.GPU并不能对图片的各种通道都能进行有效的处理。
GPU只接受通道数为一或四的图片,且数值类型为char或float。GPU不接受double类型的图片数据,否则会有异常抛出。对于三通道的图片数据,要么在图片上再加一个通道变为四通道(但这样会比较消耗GPU内存不推荐);或者,将图片按通道划分成多个单通道图片依次处理。若对于某些函数,图片中元素的实际位置不影响处理,那么比较好的处理办法是将图片直接reshape成单通道图片,例如对于PSNR的absdiff操作。

其次,关于GPU具体的代码优化。

1.GPU上面的内存分配操作需要谨慎,尽可能越少越好。因此对于被多次调用的函数来说,函数使用的局部参数变量尽可能只声明一次。例如double getPSNR_GPU_optimized()函数与double getPSNR_GPU()函数的比较,同样是在GPU上运行,前者的效率要高于后者。因为前者使用了结构体BufferPSNR,将变量 gpu::GpuMat gI1, gI2, gs, t1,t2仅做一次声明;无论函数被调用多少次,变量的内存分配却只做了一次;而后者每次调用都要做一次变量声明,这样的结果是GPU要做多次的变量临时内存分配操作,影响效率。

2.避免不必要的数据转移存储操作。例如
b.t1 = 2 * b.mu1_mu2 + C1;
GPU在计算2*b.mu1_mu2后,会将结果暂存在一个新的分配的内存区间,然后再做加运算和赋值操作;
若将代码改为如下:
gpu::multiply(b.mu1_mu2, 2, b.t1); //b.t1 = 2 * b.mu1_mu2 + C1;
gpu::add(b.t1, C1, b.t1);
则可以避免GPU这种数据转移的操作。

3.借助gpu::Stream实现异步调用。默认情况下,调用一个函数会等它执行完并返回结果。异步调用则可以执行函数,内存分配并快速返回结果。
b.gI1.upload(i1);
b.gI2.upload(i2);
gpu::Stream stream;
stream.enqueueConvert(b.gI1, b.t1, CV_32F);
stream.enqueueConvert(b.gI2, b.t2, CV_32F);
gpu::split(b.t1, b.vI1, stream);
gpu::split(b.t2, b.vI2, stream);

gI1.upload(i1);
gI2.upload(i2);
gI1.convertTo(tmp1, CV_MAKE_TYPE(CV_32F, gI1.channels()));
gI2.convertTo(tmp2, CV_MAKE_TYPE(CV_32F, gI2.channels()));
vector vI1, vI2;
gpu::split(tmp1, vI1);
gpu::split(tmp2, vI2);
以上面两部分代码为例,第二部分代码是默认的图片上传,将图片按照通道进行划分,并对每个通道进行依次操作;第一部分使用了stream进行了优化,在函数执行期间,我们仍可以进行内存分配的操作。详细可见gpu::stream

附上两份完整的代码:
double getPSNR(const Mat& I1, const Mat& I2)
{
Mat s1;
absdiff(I1, I2, s1); // |I1 - I2|
s1.convertTo(s1, CV_32F); // cannot make a square on 8 bits
s1 = s1.mul(s1); // |I1 - I2|^2
Scalar s = sum(s1); // sum elements per channel
double sse = s.val[0] + s.val[1] + s.val[2]; // sum channels
if( sse <= 1e-10) // for small values return zero
return 0;
else
{
double mse =sse /(double)(I1.channels() * I1.total());
double psnr = 10.0*log10((255*255)/mse);
return psnr;
}
}

double getPSNR_GPU_optimized(const Mat& I1, const Mat& I2, BufferPSNR& b)
{
b.gI1.upload(I1);
b.gI2.upload(I2);
b.gI1.convertTo(b.t1, CV_32F);
b.gI2.convertTo(b.t2, CV_32F);
gpu::absdiff(b.t1.reshape(1), b.t2.reshape(1), b.gs);
gpu::multiply(b.gs, b.gs, b.gs);
double sse = gpu::sum(b.gs, b.buf)[0];
if( sse <= 1e-10) // for small values return zero
return 0;
else
{
double mse = sse /(double)(I1.channels() * I1.total());
double psnr = 10.0*log10((255*255)/mse);
return psnr;
}
}

struct BufferPSNR // Optimized GPU versions
{ // Data allocations are very expensive on GPU. Use a buffer to solve: allocate once reuse later.
gpu::GpuMat gI1, gI2, gs, t1,t2;

gpu::GpuMat buf;

};

double getPSNR_GPU(const Mat& I1, const Mat& I2)
{
gpu::GpuMat gI1, gI2, gs, t1,t2;
gI1.upload(I1);
gI2.upload(I2);
gI1.convertTo(t1, CV_32F);
gI2.convertTo(t2, CV_32F);
gpu::absdiff(t1.reshape(1), t2.reshape(1), gs);
gpu::multiply(gs, gs, gs);
Scalar s = gpu::sum(gs);
double sse = s.val[0] + s.val[1] + s.val[2];
if( sse <= 1e-10) // for small values return zero
return 0;
else
{
double mse =sse /(double)(gI1.channels() * I1.total());
double psnr = 10.0*log10((255*255)/mse);
return psnr;
}
}

Scalar getMSSIM( const Mat& i1, const Mat& i2)
{
const double C1 = 6.5025, C2 = 58.5225;
/******************* INITS ************************/
int d = CV_32F;

Mat I1, I2;
i1.convertTo(I1, d);           // cannot calculate on one byte large values
i2.convertTo(I2, d);Mat I2_2   = I2.mul(I2);        // I2^2
Mat I1_2   = I1.mul(I1);        // I1^2
Mat I1_I2  = I1.mul(I2);        // I1 * I2/*************************** END INITS **********************************/Mat mu1, mu2;   // PRELIMINARY COMPUTING
GaussianBlur(I1, mu1, Size(11, 11), 1.5);
GaussianBlur(I2, mu2, Size(11, 11), 1.5);Mat mu1_2   =   mu1.mul(mu1);
Mat mu2_2   =   mu2.mul(mu2);
Mat mu1_mu2 =   mu1.mul(mu2);Mat sigma1_2, sigma2_2, sigma12;GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
sigma1_2 -= mu1_2;GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
sigma2_2 -= mu2_2;GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);
sigma12 -= mu1_mu2;/ FORMULA
Mat t1, t2, t3;t1 = 2 * mu1_mu2 + C1;
t2 = 2 * sigma12 + C2;
t3 = t1.mul(t2);              // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))t1 = mu1_2 + mu2_2 + C1;
t2 = sigma1_2 + sigma2_2 + C2;
t1 = t1.mul(t2);               // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))Mat ssim_map;
divide(t3, t1, ssim_map);      // ssim_map =  t3./t1;Scalar mssim = mean( ssim_map ); // mssim = average of ssim map
return mssim;

}

Scalar getMSSIM_GPU( const Mat& i1, const Mat& i2)
{
const float C1 = 6.5025f, C2 = 58.5225f;
/******************* INITS ************************/
gpu::GpuMat gI1, gI2, gs1, tmp1,tmp2;

gI1.upload(i1);
gI2.upload(i2);gI1.convertTo(tmp1, CV_MAKE_TYPE(CV_32F, gI1.channels()));
gI2.convertTo(tmp2, CV_MAKE_TYPE(CV_32F, gI2.channels()));vector<gpu::GpuMat> vI1, vI2;
gpu::split(tmp1, vI1);
gpu::split(tmp2, vI2);
Scalar mssim;for( int i = 0; i < gI1.channels(); ++i )
{gpu::GpuMat I2_2, I1_2, I1_I2;gpu::multiply(vI2[i], vI2[i], I2_2);        // I2^2gpu::multiply(vI1[i], vI1[i], I1_2);        // I1^2gpu::multiply(vI1[i], vI2[i], I1_I2);       // I1 * I2/*************************** END INITS **********************************/gpu::GpuMat mu1, mu2;   // PRELIMINARY COMPUTINGgpu::GaussianBlur(vI1[i], mu1, Size(11, 11), 1.5);gpu::GaussianBlur(vI2[i], mu2, Size(11, 11), 1.5);gpu::GpuMat mu1_2, mu2_2, mu1_mu2;gpu::multiply(mu1, mu1, mu1_2);gpu::multiply(mu2, mu2, mu2_2);gpu::multiply(mu1, mu2, mu1_mu2);gpu::GpuMat sigma1_2, sigma2_2, sigma12;gpu::GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);gpu::subtract(sigma1_2, mu1_2, sigma1_2); // sigma1_2 -= mu1_2;gpu::GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);gpu::subtract(sigma2_2, mu2_2, sigma2_2); // sigma2_2 -= mu2_2;gpu::GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);gpu::subtract(sigma12, mu1_mu2, sigma12); // sigma12 -= mu1_mu2;/ FORMULA gpu::GpuMat t1, t2, t3;mu1_mu2.convertTo(t1, -1, 2, C1); // t1 = 2 * mu1_mu2 + C1;sigma12.convertTo(t2, -1, 2, C2); // t2 = 2 * sigma12 + C2;gpu::multiply(t1, t2, t3);        // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))gpu::addWeighted(mu1_2, 1.0, mu2_2, 1.0, C1, t1);       // t1 = mu1_2 + mu2_2 + C1;gpu::addWeighted(sigma1_2, 1.0, sigma2_2, 1.0, C2, t2); // t2 = sigma1_2 + sigma2_2 + C2;gpu::multiply(t1, t2, t1);                              // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))gpu::GpuMat ssim_map;gpu::divide(t3, t1, ssim_map);      // ssim_map =  t3./t1;Scalar s = gpu::sum(ssim_map);mssim.val[i] = s.val[0] / (ssim_map.rows * ssim_map.cols);}
return mssim;

}
struct BufferMSSIM // Optimized GPU versions
{ // Data allocations are very expensive on GPU. Use a buffer to solve: allocate once reuse later.
gpu::GpuMat gI1, gI2, gs, t1,t2;

gpu::GpuMat I1_2, I2_2, I1_I2;
vector<gpu::GpuMat> vI1, vI2;gpu::GpuMat mu1, mu2;
gpu::GpuMat mu1_2, mu2_2, mu1_mu2;gpu::GpuMat sigma1_2, sigma2_2, sigma12;
gpu::GpuMat t3;gpu::GpuMat ssim_map;gpu::GpuMat buf;

};
Scalar getMSSIM_GPU_optimized( const Mat& i1, const Mat& i2, BufferMSSIM& b)
{
const float C1 = 6.5025f, C2 = 58.5225f;
/******************* INITS ************************/

b.gI1.upload(i1);
b.gI2.upload(i2);gpu::Stream stream;stream.enqueueConvert(b.gI1, b.t1, CV_32F);
stream.enqueueConvert(b.gI2, b.t2, CV_32F);gpu::split(b.t1, b.vI1, stream);
gpu::split(b.t2, b.vI2, stream);
Scalar mssim;gpu::GpuMat buf;for( int i = 0; i < b.gI1.channels(); ++i )
{gpu::multiply(b.vI2[i], b.vI2[i], b.I2_2, stream);        // I2^2gpu::multiply(b.vI1[i], b.vI1[i], b.I1_2, stream);        // I1^2gpu::multiply(b.vI1[i], b.vI2[i], b.I1_I2, stream);       // I1 * I2gpu::GaussianBlur(b.vI1[i], b.mu1, Size(11, 11), buf, 1.5, 0, BORDER_DEFAULT, -1, stream);gpu::GaussianBlur(b.vI2[i], b.mu2, Size(11, 11), buf, 1.5, 0, BORDER_DEFAULT, -1, stream);gpu::multiply(b.mu1, b.mu1, b.mu1_2, stream);gpu::multiply(b.mu2, b.mu2, b.mu2_2, stream);gpu::multiply(b.mu1, b.mu2, b.mu1_mu2, stream);gpu::GaussianBlur(b.I1_2, b.sigma1_2, Size(11, 11), buf, 1.5, 0, BORDER_DEFAULT, -1, stream);gpu::subtract(b.sigma1_2, b.mu1_2, b.sigma1_2, gpu::GpuMat(), -1, stream);//b.sigma1_2 -= b.mu1_2;  - This would result in an extra data transfer operationgpu::GaussianBlur(b.I2_2, b.sigma2_2, Size(11, 11), buf, 1.5, 0, BORDER_DEFAULT, -1, stream);gpu::subtract(b.sigma2_2, b.mu2_2, b.sigma2_2, gpu::GpuMat(), -1, stream);//b.sigma2_2 -= b.mu2_2;gpu::GaussianBlur(b.I1_I2, b.sigma12, Size(11, 11), buf, 1.5, 0, BORDER_DEFAULT, -1, stream);gpu::subtract(b.sigma12, b.mu1_mu2, b.sigma12, gpu::GpuMat(), -1, stream);//b.sigma12 -= b.mu1_mu2;//here too it would be an extra data transfer due to call of operator*(Scalar, Mat)gpu::multiply(b.mu1_mu2, 2, b.t1, 1, -1, stream); //b.t1 = 2 * b.mu1_mu2 + C1;gpu::add(b.t1, C1, b.t1, gpu::GpuMat(), -1, stream);gpu::multiply(b.sigma12, 2, b.t2, 1, -1, stream); //b.t2 = 2 * b.sigma12 + C2;gpu::add(b.t2, C2, b.t2, gpu::GpuMat(), -12, stream);gpu::multiply(b.t1, b.t2, b.t3, 1, -1, stream);     // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))gpu::add(b.mu1_2, b.mu2_2, b.t1, gpu::GpuMat(), -1, stream);gpu::add(b.t1, C1, b.t1, gpu::GpuMat(), -1, stream);gpu::add(b.sigma1_2, b.sigma2_2, b.t2, gpu::GpuMat(), -1, stream);gpu::add(b.t2, C2, b.t2, gpu::GpuMat(), -1, stream);gpu::multiply(b.t1, b.t2, b.t1, 1, -1, stream);     // t1 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))gpu::divide(b.t3, b.t1, b.ssim_map, 1, -1, stream);      // ssim_map =  t3./t1;stream.waitForCompletion();Scalar s = gpu::sum(b.ssim_map, b.buf);mssim.val[i] = s.val[0] / (b.ssim_map.rows * b.ssim_map.cols);
}
return mssim;

}

opencv的GPU编程(一)相关推荐

  1. android gpu 编程,Android设备上的GPU编程

    1)Programming Smartphone's GPU and programming other GPU (Nvidia GeForce 9 for example) are equal? 不 ...

  2. NVIDIA GPU编程系列课程(CUDA编程)

    NVIDIA网站有一部分GPU编程系列的课程,具体的课程地址为: https://www.nvidia.cn/developer/online-training/community-training/ ...

  3. GPU编程和流式多处理器(二)

    GPU编程和流式多处理器(二) 2. 整数支持 SM具有32位整数运算的完整补充. • 加法运算符的可选否定加法 • 乘法与乘法加法 • 整数除法 • 逻辑运算 • 条件码操作 • to/from浮点 ...

  4. GPU编程和流式多处理器

    GPU编程和流式多处理器 流式多处理器(SM)是运行CUDA内核的GPU的一部分.本章重点介绍SM的指令集功能. 流式多处理器(SM)是运行我们的CUDA内核的GPU的一部分.每个SM包含以下内容. ...

  5. opencv上gpu版surf特征点与orb特征点提取及匹配实例

    opencv上gpu版surf特征点与orb特征点提取及匹配实例 标签: gpu版surfgpu orbsurf和orbgpu surf及orbsurf orb gpu 2016-09-25 23:4 ...

  6. 《多核与GPU编程:工具、方法及实践》----1.5 并行程序性能的预测与测量

    本节书摘来自华章出版社<多核与GPU编程:工具.方法及实践>一书中的第1章,第1.5节, 作 者 Multicore and GPU Programming: An Integrated ...

  7. GPU 编程入门到精通(五)之 GPU 程序优化进阶

    版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 博主由于工作当中的需要,开始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识,鉴于之前没有接触过 GP ...

  8. GPU 编程入门到精通(四)之 GPU 程序优化

    版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 博主由于工作当中的需要,开始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识,鉴于之前没有接触过 GP ...

  9. GPU 编程入门到精通(三)之 第一个 GPU 程序

    博主由于工作当中的需要,开始学习 GPU 上面的编程,主要涉及到的是基于 GPU 的深度学习方面的知识,鉴于之前没有接触过 GPU 编程,因此在这里特地学习一下 GPU 上面的编程.有志同道合的小伙伴 ...

最新文章

  1. CSS布局之-水平垂直居中
  2. 编译与部署dubbo管控平台dubbo-admin
  3. jlink怎么调试linux程序_纯Linux下的 ARM裸机调试环境搭建(GDB + JLink)
  4. Win32ASM学习[19]:结构与联合
  5. Qt之QThreadPool和QRunnable
  6. DataGridView控件的使用 1207
  7. (38)时钟延迟约束
  8. SQL中DB2数据库修改某字段长度或类型
  9. LTE学习:CCE(control channel element)控制信道单元(1)
  10. HDU 3468 HDOJ 3468 A Simple Problem with Integers ACM 3468 IN HDU
  11. AIDA64 Business Edition 5.97.4600 多语言绿色版
  12. 汽车车载电子设备可靠性标准及项目汇总
  13. 粪斗吧 ! 骚年 !
  14. 修改layui绿色为蓝色
  15. matlab中dcd是什么,dcd是什么意思
  16. 结构化设计(实验二)
  17. Spark MLlib数据挖掘2--基础统计分析
  18. 与openSNP分享您的遗传故事
  19. 华为手机设置微信气泡
  20. 1. R语言中grep函数和gsub()函数的使用

热门文章

  1. 应该如何学习oracle数据库
  2. 【收藏】身份证号、手机号、姓名等数据脱敏信息工具类介绍
  3. MongoDB备份恢复—mongodump/mongorestoremongoexport/mongoimport
  4. 2020年ai顶会时间表_2020年企业采用AI
  5. 驰骋工作流jeesite4_jflow的安装讲解
  6. 学生网页成品展示 静态HTML网页设计制作技巧 dreamweaver作品展示2022.3期
  7. 【工程伦理】脑机接口技术中的伦理问题分析
  8. 压缩工具squashfs安装
  9. mobaXterm 使用sftp上传文件速度慢或者无法上传问题
  10. 机顶盒从ubuntu16更换到archLinux2021最新版,搞个远程下载支持智能电视直接播放