▶ CPU 图像卷积,共四种方法。分别为基本串行,使用模板,使用局部内存,使用AVX指令优化

● 全部的代码,仅在主函数中选择调用的函数名即可。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. #include <opencv2/opencv.hpp>
  5.  
  6. const char *inputFile = "R:/1.png";
  7. const char *outputFile = "R:/2.png";
  8.  
  9. bool floatEq(const float a, const float b)// 相等返回 1
  10. {
  11. if (b == )
  12. return fabs(a) < 0.001;
  13. return fabs(a / b - ) < 0.001;
  14. }
  15.  
  16. void convolution01(const float *input, float *output, const int inputRow, const int inputCol,
  17. const float *filter, const int filterWidth)
  18. {
  19. const int halfFilterWidth = filterWidth / ;
  20. int row, col, rr, cc;
  21. float sum;
  22.  
  23. //memset(output, 0, sizeof(float) * inputRow * inputCol); // 使用 memset 将 output 全部凃成 0
  24.  
  25. #pragma omp parallel for num_threads(8) default(none) shared(halfFilterWidth, output, inputRow, inputCol) private(row, col)
  26. for (row = ; row < halfFilterWidth; row++) // 人工将边角涂成 0
  27. {
  28. for (col = ; col < inputCol; col++)
  29. output[row*inputCol + col] = output[(inputRow - - row)*inputCol + col] = ;
  30. }
  31. #pragma omp parallel for num_threads(8) default(none) shared(halfFilterWidth, output, inputRow, inputCol) private(row, col)
  32. for (row = halfFilterWidth; row < inputRow - halfFilterWidth; row++)
  33. {
  34. for (col = ; col < halfFilterWidth; col++)
  35. output[row*inputCol + col] = output[row*inputCol + inputCol - - col] = ;
  36. }
  37.  
  38. #pragma omp parallel for num_threads(8) default(none) shared(halfFilterWidth, input, output, inputRow, inputCol, filter) private(row, col, rr, cc, sum)
  39. for (row = halfFilterWidth; row < inputRow - halfFilterWidth; row++)// 内部计算部分
  40. {
  41. for (col = halfFilterWidth; col < inputCol - halfFilterWidth; col++)
  42. {
  43. for (sum = 0.0f, rr = -halfFilterWidth; rr <= halfFilterWidth; rr++)
  44. {
  45. for (cc = -halfFilterWidth; cc <= halfFilterWidth; cc++)
  46. sum += filter[(rr + halfFilterWidth) * filterWidth + cc + halfFilterWidth] * input[(row + rr)*inputCol + col + cc];
  47. }
  48. output[row * inputCol + col] = sum;
  49. }
  50. }
  51. /*
  52. for (row = 0; row < inputRow; row++) // 全范围循环,在最里层判断
  53. {
  54. for (col = 0; col < inputCol; col++)
  55. {
  56. if (row < halfFilterWidth || row >= inputRow - halfFilterWidth || col < halfFilterWidth || col >= inputCol - halfFilterWidth)
  57. {
  58. output[row * inputCol + col] = 0;
  59. continue;
  60. }
  61. for (sum = 0.0f, rr = -halfFilterWidth; rr <= halfFilterWidth; rr++)
  62. {
  63. for (cc = -halfFilterWidth; cc <= halfFilterWidth; cc++)
  64. sum += filter[(rr + halfFilterWidth) * filterWidth + cc + halfFilterWidth] * input[(row + rr)*inputCol + col + cc];
  65. }
  66. output[row * inputCol + col] = sum;
  67.  
  68. }
  69. }
  70. */
  71. }
  72.  
  73. template<int filterWidth>
  74. void convolution02(const float *input, float *output, const int inputRow, const int inputCol, const float *filter)// 卷积宽度作为模板
  75. {
  76. const int halfFilterWidth = filterWidth / ;
  77. int row, col, rr, cc;
  78. float sum;
  79.  
  80. memset(output, , sizeof(float) * inputRow * inputCol);
  81. #pragma omp parallel for num_threads(8) default(none) shared(halfFilterWidth, input, output, inputRow, inputCol, filter) private(row, col, rr, cc, sum)
  82. for (row = halfFilterWidth; row < inputRow - halfFilterWidth; row++)
  83. {
  84. for (col = halfFilterWidth; col < inputCol - halfFilterWidth; col++)
  85. {
  86. for (sum = 0.0f, rr = -halfFilterWidth; rr <= halfFilterWidth; rr++)
  87. {
  88. for (cc = -halfFilterWidth; cc <= halfFilterWidth; cc++)
  89. sum += filter[(rr + halfFilterWidth) * filterWidth + cc + halfFilterWidth] * input[(row + rr)*inputCol + col + cc];
  90. }
  91. output[row * inputCol + col] = sum;
  92. }
  93. }
  94. }
  95.  
  96. template<int filterWidth, int blockRow, int blockCol>
  97. void convolution03(const float *input, float *output, const int inputRow, const int inputCol, const float *filter)// 使用局部内存块
  98. {
  99. const int halfFilterWidth = filterWidth / ;
  100. int row, col, rr, cc, i, j;
  101. float filterElement;
  102.  
  103. if (inputRow % blockRow || inputCol % blockCol) // 要求图片长宽为局部内存块的整数倍
  104. {
  105. printf("Failed, outputRow %% blockRow || outputCol %% blockCol\n");
  106. return;
  107. }
  108.  
  109. memset(output, , sizeof(float) * inputRow * inputCol);
  110. #pragma omp parallel for num_threads(8)
  111. for (row = halfFilterWidth; row < inputRow - halfFilterWidth; row += blockRow)
  112. {
  113. for (col = halfFilterWidth; col < inputCol - halfFilterWidth; col += blockCol)
  114. {
  115. float sum[blockRow * blockCol] = { 0.0f };
  116. for (rr = -halfFilterWidth; rr <= halfFilterWidth; rr++)
  117. {
  118. for (cc = -halfFilterWidth; cc <= halfFilterWidth; cc++)
  119. {
  120. filterElement = filter[(rr + halfFilterWidth) * filterWidth + cc + halfFilterWidth];
  121. for (i = ; i < blockRow; i++)
  122. {
  123. for (j = ; j < blockCol; j++)
  124. {
  125. if (row + rr + i >= inputRow || col + cc + j >= inputCol)
  126. break;
  127. sum[i * blockCol + j] += filterElement * input[(row + rr + i) * inputCol + col + cc + j];
  128. }
  129. }
  130. }
  131. }
  132. for (i = ; i < blockRow; i++)
  133. {
  134. for (j = ; j < blockCol; j++)
  135. {
  136. if (row + i >= inputRow || col + j >= inputCol)
  137. continue;
  138. output[(row + i) * inputCol + col + j] = sum[i * blockCol + j];
  139. }
  140. }
  141. }
  142. }
  143. }
  144.  
  145. template<int filterWidth, int blockRow, int blockCol>
  146. void convolution04(const float *input, float *output, const int inputRow, const int inputCol, const float *filter)// 使用 AVX 指令扩展
  147. {
  148. const int halfFilterWidth = filterWidth / ;
  149. int row, col, rr, cc, i, j;
  150.  
  151. if (inputRow % blockRow || inputCol % (blockCol * ))
  152. {
  153. printf("Failed, inputRow %% blockRow || inputCol %% blockCol\n");
  154. return;
  155. }
  156.  
  157. memset(output, , sizeof(float) * inputRow * inputCol);
  158. #pragma omp parallel for num_threads(8)
  159. for (row = halfFilterWidth; row < inputRow - halfFilterWidth; row += blockRow)
  160. {
  161. for (col = halfFilterWidth; col < inputCol - halfFilterWidth; col += blockCol * )
  162. {
  163. __m256 sum[blockRow * blockCol] = {_mm256_setzero_ps()};
  164. for (rr = -halfFilterWidth; rr <= halfFilterWidth; rr++)
  165. {
  166. for (cc = -halfFilterWidth; cc <= halfFilterWidth; cc++)
  167. {
  168. __m256 filterElement = _mm256_broadcast_ss(filter + (rr + halfFilterWidth) * filterWidth + cc + halfFilterWidth);
  169. for (i = ; i < blockRow; i++)
  170. {
  171. for (j = ; j < blockCol; j++)
  172. {
  173. //if (row + rr + i >= inputRow || col + cc + j * 8 >= inputCol)// 在局部内存块较大时需要越界检查
  174. // continue;
  175. __m256 imageElement = _mm256_loadu_ps(input + (row + rr + i)*inputCol + col + cc + j * );
  176. sum[i * blockCol + j] = _mm256_fmadd_ps(filterElement, imageElement, sum[i * blockCol + j]);
  177. }
  178. }
  179. }
  180. }
  181. for (i = ; i < blockRow; i++)
  182. {
  183. for (j = ; j < blockCol; j++)
  184. {
  185. //if (row + i >= inputRow || col + j * 8 >= inputCol)
  186. // continue;
  187. _mm256_storeu_ps(output + (row + i)*inputCol + col + j * , sum[i * blockCol + j]);
  188. }
  189. }
  190. }
  191. }
  192. }
  193.  
  194. int main()
  195. {
  196. int i, k;
  197. clock_t time;
  198. float filterSum;
  199.  
  200. // 卷积窗口相关
  201. const int filterWidth = , filterSize = filterWidth * filterWidth, halfFilterWidth = filterWidth / ;
  202. float filter[filterSize] =
  203. {// 模糊窗口
  204. , , , , ,
  205. , .f / , .f / , .f / , ,
  206. , .f / , .f / , .f / , ,
  207. , .f / , .f / , .f / , ,
  208. , , , ,
  209. };
  210. for (filterSum = 0.0f, i = ; i < filterSize; filterSum += filter[i++]);
  211. if (!floatEq(filterSum, ))// 非归零的卷积窗口(如模糊)需要归一化
  212. for (i = ; i < filterSize; filter[i] /= filterSum, i++);
  213.  
  214. // 图片相关
  215. cv::Mat input = cv::imread(inputFile), output = input, channel[];
  216. cv::split(input, channel);
  217. const int inputRow = input.rows, inputCol = input.cols, inputDataSize = inputRow * inputCol;
  218. float *inputData = (float*)malloc(sizeof(float) * inputDataSize);
  219. float *outputData = (float*)malloc(sizeof(float) * inputDataSize);
  220.  
  221. for (k = ; k < ; k++)// 三个通道,分别为蓝、绿、红
  222. {
  223. for (i = ; i < inputRow * inputCol; inputData[i] = (float)channel[k].data[i], i++);
  224. time = clock();
  225. convolution01(inputData, outputData, inputRow, inputCol, (const float *)filter, filterWidth);
  226. //convolution02<filterWidth>(inputData, outputData, inputRow, inputCol, filter);
  227. //convolution03<filterWidth, 4, 4>(inputData, outputData, inputRow, inputCol, filter);
  228. //convolution04<filterWidth, 4, 4>(inputData, outputData, inputRow, inputCol, filter);
  229. time = clock() - time;
  230. printf("Time for channel[%d]:%d ms\n", k, time);
  231. for (i = ; i < inputRow * inputCol; channel[k].data[i] = (unsigned char)outputData[i], i++);
  232. }
  233.  
  234. cv::merge(channel, , output);
  235. cv::imwrite(outputFile, output);
  236. //imshow("merge", output);
  237. //cv::waitKey(0);
  238. free(inputData);
  239. free(outputData);
  240. printf("\nFinished.\n");
  241. getchar();
  242. return ;
  243. }

● 输出结果,使用一张 4608 × 6656 的图片(bmp87.7MB)进行测试,使用主函数中那个边长为5、实际窗口长度为 3 的均值窗口。图片太大喘不上来,偷梁换柱成小图看效果

     

■ 计时结果

  1. // convolution01,memset + 内部计算,无 OpenMP
  2. Time for channel[]: ms
  3. Time for channel[]: ms
  4. Time for channel[]: ms
  5.  
  6. Finished.
  7.  
  8. // convolution01,手动除边 + 内部计算,无 OpenMP
  9. Time for channel[]: ms
  10. Time for channel[]: ms
  11. Time for channel[]: ms
  12.  
  13. Finished.
  14.  
  15. // convolution01,循环内判断,无 OpenMP
  16. Time for channel[]: ms
  17. Time for channel[]: ms
  18. Time for channel[]: ms
  19.  
  20. Finished.
  21.  
  22. // convolution01,手动除边 + 内部计算,有 OpenMP
  23. Time for channel[]: ms
  24. Time for channel[]: ms
  25. Time for channel[]: ms
  26.  
  27. Finished.
  28.  
  29. // convolution02,有 OpenMP
  30. Time for channel[]: ms
  31. Time for channel[]: ms
  32. Time for channel[]: ms
  33.  
  34. Finished.
  35.  
  36. // convolution03<filterWidth, 4, 4>,无 OpenMP
  37. Time for channel[]: ms
  38. Time for channel[]: ms
  39. Time for channel[]: ms
  40.  
  41. Finished.
  42.  
  43. // convolution04<filterWidth, 4, 4>,无 OpenMP
  44. Time for channel[]: ms
  45. Time for channel[]: ms
  46. Time for channel[]: ms
  47.  
  48. Finished.

■ 没法给 convolution03 和 convolution04 加 OpenMP,一加就各种内存冲突,便捷判断都挡不住。

OpenCL 图像卷积 3 使用 CPU的更多相关文章

  1. OpenCL 图像卷积 2

    ▶ 上一篇图像卷积 http://www.cnblogs.com/cuancuancuanhao/p/8535569.html.这篇使用了 OpenCV 从文件读取彩色的 jpeg 图像,进行边缘检测 ...

  2. OpenCL 图像卷积 1

    ▶ 书上的代码改进而成,从文件读入一张 256 阶灰度图,按照给定的卷积窗口计算卷积,并输出到文件中. ● 代码,使用 9 格的均值窗口,居然硬读写 .bmp 文件,算是了解一下该文件的具体格式,留作 ...

  3. SSE图像算法优化系列十一:使用FFT变换实现图像卷积。

    本文重点主要不在于FFT的SSE优化,而在于使用FFT实现快速卷积的相关技巧和过程. 关于FFT变换,有很多参考的代码,特别是对于长度为2的整数次幂的序列,实现起来也是非常简易的,而对于非2次幂的序列 ...

  4. 图像卷积、相关以及在MATLAB中的操作

    图像卷积.相关以及在MATLAB中的操作 2016年7月11日 20:34:35, By ChrisZZ 区分卷积和相关 图像处理中常常需要用一个滤波器做空间滤波操作.空间滤波操作有时候也被叫做卷积滤 ...

  5. zz图像卷积与滤波的一些知识点

    Xinwei: 写的通俗易懂,终于让我这个不搞CV.不搞图像的外行理解卷积和滤波了. 图像卷积与滤波的一些知识点 zouxy09@qq.com http://blog.csdn.net/zouxy09 ...

  6. 对抗生成网络-图像卷积-mnist数据生成(代码) 1.tf.layers.conv2d(卷积操作) 2.tf.layers.conv2d_transpose(反卷积操作) 3.tf.layers.batch_normalize(归一化操作) 4.tf.maximum(用于lrelu) 5.tf.train_variable(训练中所有参数) 6.np.random.uniform(生成正态数据

    1. tf.layers.conv2d(input, filter, kernel_size, stride, padding) # 进行卷积操作 参数说明:input输入数据, filter特征图的 ...

  7. UFLDL教程笔记及练习答案五(自编码线性解码器与处理大型图像**卷积与池化)

    自己主动编码线性解码器 自己主动编码线性解码器主要是考虑到稀疏自己主动编码器最后一层输出假设用sigmoid函数.因为稀疏自己主动编码器学习是的输出等于输入.simoid函数的值域在[0,1]之间,这 ...

  8. TensorFlow实现图像卷积并可视化示例

    图片尺寸要自己修改. 看起来好像没啥意思,不知道下一步能干什么,先卷了再说.由于weights是随机生成的(tf.random_normal作用:用于从服从指定正太分布的数值中取出随机数),所以每次卷 ...

  9. opencv:图像卷积

    卷积基本概念 C++代码实现卷积 #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; u ...

随机推荐

  1. 51Nod 1079:中国剩余定理

    1079 中国剩余定理  基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题  收藏  关注 一个正整数K,给出K Mod 一些质数的结果,求符合条件的最小的K.例如,K % ...

  2. mac OS 安装 scikit-learn

    最近用来做实验,使用python时发现scikit-learn提供的库非常好用.因此,在电脑上果断下载安装: step1: sudo easy_install pip step2: sudo pip ...

  3. tp5.1 错误 No input file specified.

    http://www.xxxx.com/admin/index/index 出现错误:No input file specified. 一.方法 与php版本有关  PHP版本5.6以上都会出现这个问 ...

  4. dgraph 数据加载

    dgraph 可以方便的进行大量的数据加载 下载rdf 文件 wget "https://github.com/dgraph-io/tutorial/blob/master/resource ...

  5. POJ_2299 Ultra-QuickSort【归并排序】

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u013912596/article/details/35655703 题目链接:http://poj ...

  6. Spark的启动进程详解

    Master和Worker是执行任务之前存在的进程 (类似于公司) Driver和Excutor是任务执行之后存在的进程(类似于公司接到项目后才成立的项目小组) 启动步骤: 启动Master资源管理进 ...

  7. git revert回退时提示One or more files are in a conflicted state

    解决代码冲突 如果commit时出现“You have to update your work copy first.”红色警告,说明版本库中的此文件已经被其他人修改了. 请先点“ok”按钮退出.执行 ...

  8. jquery禁止复制、禁用右键、文本选择功能、复制按键

    本文章介绍的jquery禁用右键.文本选择功能.复制按键的实现它可以兼容浏览器有IE.firefox.谷歌浏览器,各位朋友可参考.IE浏览器是指以IE为核心的浏览器也支持,有360,QQ等 代码如下: ...

  9. Linux(Centos7)下安装 zookeeper docker版 集群

    为了省去麻烦的软件安装,现在开发环境需要的软件越来越习惯于docker安装了,先看下安装后的截图,开发环境正在启动的容器 1.首先系统需要先支持docker …… 由于之前安装几次都没有做流程记录,在 ...

  10. innotop监控mysql

    InnoTop 是一个系统活动报告,类似于Linux性能工具,它与Linux的top命令相仿,并参考mytop工具而设计. 它专门用后监控InnoDB性能和MySQL服务器.主要用于监控事务,死锁,外 ...