OpenCL 图像卷积 3 使用 CPU
▶ CPU 图像卷积,共四种方法。分别为基本串行,使用模板,使用局部内存,使用AVX指令优化
● 全部的代码,仅在主函数中选择调用的函数名即可。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <opencv2/opencv.hpp> const char *inputFile = "R:/1.png";
const char *outputFile = "R:/2.png"; bool floatEq(const float a, const float b)// 相等返回 1
{
if (b == )
return fabs(a) < 0.001;
return fabs(a / b - ) < 0.001;
} void convolution01(const float *input, float *output, const int inputRow, const int inputCol,
const float *filter, const int filterWidth)
{
const int halfFilterWidth = filterWidth / ;
int row, col, rr, cc;
float sum; //memset(output, 0, sizeof(float) * inputRow * inputCol); // 使用 memset 将 output 全部凃成 0 #pragma omp parallel for num_threads(8) default(none) shared(halfFilterWidth, output, inputRow, inputCol) private(row, col)
for (row = ; row < halfFilterWidth; row++) // 人工将边角涂成 0
{
for (col = ; col < inputCol; col++)
output[row*inputCol + col] = output[(inputRow - - row)*inputCol + col] = ;
}
#pragma omp parallel for num_threads(8) default(none) shared(halfFilterWidth, output, inputRow, inputCol) private(row, col)
for (row = halfFilterWidth; row < inputRow - halfFilterWidth; row++)
{
for (col = ; col < halfFilterWidth; col++)
output[row*inputCol + col] = output[row*inputCol + inputCol - - col] = ;
} #pragma omp parallel for num_threads(8) default(none) shared(halfFilterWidth, input, output, inputRow, inputCol, filter) private(row, col, rr, cc, sum)
for (row = halfFilterWidth; row < inputRow - halfFilterWidth; row++)// 内部计算部分
{
for (col = halfFilterWidth; col < inputCol - halfFilterWidth; col++)
{
for (sum = 0.0f, rr = -halfFilterWidth; rr <= halfFilterWidth; rr++)
{
for (cc = -halfFilterWidth; cc <= halfFilterWidth; cc++)
sum += filter[(rr + halfFilterWidth) * filterWidth + cc + halfFilterWidth] * input[(row + rr)*inputCol + col + cc];
}
output[row * inputCol + col] = sum;
}
}
/*
for (row = 0; row < inputRow; row++) // 全范围循环,在最里层判断
{
for (col = 0; col < inputCol; col++)
{
if (row < halfFilterWidth || row >= inputRow - halfFilterWidth || col < halfFilterWidth || col >= inputCol - halfFilterWidth)
{
output[row * inputCol + col] = 0;
continue;
}
for (sum = 0.0f, rr = -halfFilterWidth; rr <= halfFilterWidth; rr++)
{
for (cc = -halfFilterWidth; cc <= halfFilterWidth; cc++)
sum += filter[(rr + halfFilterWidth) * filterWidth + cc + halfFilterWidth] * input[(row + rr)*inputCol + col + cc];
}
output[row * inputCol + col] = sum; }
}
*/
} template<int filterWidth>
void convolution02(const float *input, float *output, const int inputRow, const int inputCol, const float *filter)// 卷积宽度作为模板
{
const int halfFilterWidth = filterWidth / ;
int row, col, rr, cc;
float sum; memset(output, , sizeof(float) * inputRow * inputCol);
#pragma omp parallel for num_threads(8) default(none) shared(halfFilterWidth, input, output, inputRow, inputCol, filter) private(row, col, rr, cc, sum)
for (row = halfFilterWidth; row < inputRow - halfFilterWidth; row++)
{
for (col = halfFilterWidth; col < inputCol - halfFilterWidth; col++)
{
for (sum = 0.0f, rr = -halfFilterWidth; rr <= halfFilterWidth; rr++)
{
for (cc = -halfFilterWidth; cc <= halfFilterWidth; cc++)
sum += filter[(rr + halfFilterWidth) * filterWidth + cc + halfFilterWidth] * input[(row + rr)*inputCol + col + cc];
}
output[row * inputCol + col] = sum;
}
}
} template<int filterWidth, int blockRow, int blockCol>
void convolution03(const float *input, float *output, const int inputRow, const int inputCol, const float *filter)// 使用局部内存块
{
const int halfFilterWidth = filterWidth / ;
int row, col, rr, cc, i, j;
float filterElement; if (inputRow % blockRow || inputCol % blockCol) // 要求图片长宽为局部内存块的整数倍
{
printf("Failed, outputRow %% blockRow || outputCol %% blockCol\n");
return;
} memset(output, , sizeof(float) * inputRow * inputCol);
#pragma omp parallel for num_threads(8)
for (row = halfFilterWidth; row < inputRow - halfFilterWidth; row += blockRow)
{
for (col = halfFilterWidth; col < inputCol - halfFilterWidth; col += blockCol)
{
float sum[blockRow * blockCol] = { 0.0f };
for (rr = -halfFilterWidth; rr <= halfFilterWidth; rr++)
{
for (cc = -halfFilterWidth; cc <= halfFilterWidth; cc++)
{
filterElement = filter[(rr + halfFilterWidth) * filterWidth + cc + halfFilterWidth];
for (i = ; i < blockRow; i++)
{
for (j = ; j < blockCol; j++)
{
if (row + rr + i >= inputRow || col + cc + j >= inputCol)
break;
sum[i * blockCol + j] += filterElement * input[(row + rr + i) * inputCol + col + cc + j];
}
}
}
}
for (i = ; i < blockRow; i++)
{
for (j = ; j < blockCol; j++)
{
if (row + i >= inputRow || col + j >= inputCol)
continue;
output[(row + i) * inputCol + col + j] = sum[i * blockCol + j];
}
}
}
}
} template<int filterWidth, int blockRow, int blockCol>
void convolution04(const float *input, float *output, const int inputRow, const int inputCol, const float *filter)// 使用 AVX 指令扩展
{
const int halfFilterWidth = filterWidth / ;
int row, col, rr, cc, i, j; if (inputRow % blockRow || inputCol % (blockCol * ))
{
printf("Failed, inputRow %% blockRow || inputCol %% blockCol\n");
return;
} memset(output, , sizeof(float) * inputRow * inputCol);
#pragma omp parallel for num_threads(8)
for (row = halfFilterWidth; row < inputRow - halfFilterWidth; row += blockRow)
{
for (col = halfFilterWidth; col < inputCol - halfFilterWidth; col += blockCol * )
{
__m256 sum[blockRow * blockCol] = {_mm256_setzero_ps()};
for (rr = -halfFilterWidth; rr <= halfFilterWidth; rr++)
{
for (cc = -halfFilterWidth; cc <= halfFilterWidth; cc++)
{
__m256 filterElement = _mm256_broadcast_ss(filter + (rr + halfFilterWidth) * filterWidth + cc + halfFilterWidth);
for (i = ; i < blockRow; i++)
{
for (j = ; j < blockCol; j++)
{
//if (row + rr + i >= inputRow || col + cc + j * 8 >= inputCol)// 在局部内存块较大时需要越界检查
// continue;
__m256 imageElement = _mm256_loadu_ps(input + (row + rr + i)*inputCol + col + cc + j * );
sum[i * blockCol + j] = _mm256_fmadd_ps(filterElement, imageElement, sum[i * blockCol + j]);
}
}
}
}
for (i = ; i < blockRow; i++)
{
for (j = ; j < blockCol; j++)
{
//if (row + i >= inputRow || col + j * 8 >= inputCol)
// continue;
_mm256_storeu_ps(output + (row + i)*inputCol + col + j * , sum[i * blockCol + j]);
}
}
}
}
} int main()
{
int i, k;
clock_t time;
float filterSum; // 卷积窗口相关
const int filterWidth = , filterSize = filterWidth * filterWidth, halfFilterWidth = filterWidth / ;
float filter[filterSize] =
{// 模糊窗口
, , , , ,
, .f / , .f / , .f / , ,
, .f / , .f / , .f / , ,
, .f / , .f / , .f / , ,
, , , ,
};
for (filterSum = 0.0f, i = ; i < filterSize; filterSum += filter[i++]);
if (!floatEq(filterSum, ))// 非归零的卷积窗口(如模糊)需要归一化
for (i = ; i < filterSize; filter[i] /= filterSum, i++); // 图片相关
cv::Mat input = cv::imread(inputFile), output = input, channel[];
cv::split(input, channel);
const int inputRow = input.rows, inputCol = input.cols, inputDataSize = inputRow * inputCol;
float *inputData = (float*)malloc(sizeof(float) * inputDataSize);
float *outputData = (float*)malloc(sizeof(float) * inputDataSize); for (k = ; k < ; k++)// 三个通道,分别为蓝、绿、红
{
for (i = ; i < inputRow * inputCol; inputData[i] = (float)channel[k].data[i], i++);
time = clock();
convolution01(inputData, outputData, inputRow, inputCol, (const float *)filter, filterWidth);
//convolution02<filterWidth>(inputData, outputData, inputRow, inputCol, filter);
//convolution03<filterWidth, 4, 4>(inputData, outputData, inputRow, inputCol, filter);
//convolution04<filterWidth, 4, 4>(inputData, outputData, inputRow, inputCol, filter);
time = clock() - time;
printf("Time for channel[%d]:%d ms\n", k, time);
for (i = ; i < inputRow * inputCol; channel[k].data[i] = (unsigned char)outputData[i], i++);
} cv::merge(channel, , output);
cv::imwrite(outputFile, output);
//imshow("merge", output);
//cv::waitKey(0);
free(inputData);
free(outputData);
printf("\nFinished.\n");
getchar();
return ;
}
● 输出结果,使用一张 4608 × 6656 的图片(bmp87.7MB)进行测试,使用主函数中那个边长为5、实际窗口长度为 3 的均值窗口。图片太大喘不上来,偷梁换柱成小图看效果
■ 计时结果
// convolution01,memset + 内部计算,无 OpenMP
Time for channel[]: ms
Time for channel[]: ms
Time for channel[]: ms Finished. // convolution01,手动除边 + 内部计算,无 OpenMP
Time for channel[]: ms
Time for channel[]: ms
Time for channel[]: ms Finished. // convolution01,循环内判断,无 OpenMP
Time for channel[]: ms
Time for channel[]: ms
Time for channel[]: ms Finished. // convolution01,手动除边 + 内部计算,有 OpenMP
Time for channel[]: ms
Time for channel[]: ms
Time for channel[]: ms Finished. // convolution02,有 OpenMP
Time for channel[]: ms
Time for channel[]: ms
Time for channel[]: ms Finished. // convolution03<filterWidth, 4, 4>,无 OpenMP
Time for channel[]: ms
Time for channel[]: ms
Time for channel[]: ms Finished. // convolution04<filterWidth, 4, 4>,无 OpenMP
Time for channel[]: ms
Time for channel[]: ms
Time for channel[]: ms Finished.
■ 没法给 convolution03 和 convolution04 加 OpenMP,一加就各种内存冲突,便捷判断都挡不住。
■
OpenCL 图像卷积 3 使用 CPU的更多相关文章
- OpenCL 图像卷积 2
▶ 上一篇图像卷积 http://www.cnblogs.com/cuancuancuanhao/p/8535569.html.这篇使用了 OpenCV 从文件读取彩色的 jpeg 图像,进行边缘检测 ...
- OpenCL 图像卷积 1
▶ 书上的代码改进而成,从文件读入一张 256 阶灰度图,按照给定的卷积窗口计算卷积,并输出到文件中. ● 代码,使用 9 格的均值窗口,居然硬读写 .bmp 文件,算是了解一下该文件的具体格式,留作 ...
- SSE图像算法优化系列十一:使用FFT变换实现图像卷积。
本文重点主要不在于FFT的SSE优化,而在于使用FFT实现快速卷积的相关技巧和过程. 关于FFT变换,有很多参考的代码,特别是对于长度为2的整数次幂的序列,实现起来也是非常简易的,而对于非2次幂的序列 ...
- 图像卷积、相关以及在MATLAB中的操作
图像卷积.相关以及在MATLAB中的操作 2016年7月11日 20:34:35, By ChrisZZ 区分卷积和相关 图像处理中常常需要用一个滤波器做空间滤波操作.空间滤波操作有时候也被叫做卷积滤 ...
- zz图像卷积与滤波的一些知识点
Xinwei: 写的通俗易懂,终于让我这个不搞CV.不搞图像的外行理解卷积和滤波了. 图像卷积与滤波的一些知识点 zouxy09@qq.com http://blog.csdn.net/zouxy09 ...
- 对抗生成网络-图像卷积-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特征图的 ...
- UFLDL教程笔记及练习答案五(自编码线性解码器与处理大型图像**卷积与池化)
自己主动编码线性解码器 自己主动编码线性解码器主要是考虑到稀疏自己主动编码器最后一层输出假设用sigmoid函数.因为稀疏自己主动编码器学习是的输出等于输入.simoid函数的值域在[0,1]之间,这 ...
- TensorFlow实现图像卷积并可视化示例
图片尺寸要自己修改. 看起来好像没啥意思,不知道下一步能干什么,先卷了再说.由于weights是随机生成的(tf.random_normal作用:用于从服从指定正太分布的数值中取出随机数),所以每次卷 ...
- opencv:图像卷积
卷积基本概念 C++代码实现卷积 #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; u ...
随机推荐
- 20155117王震宇 2016-2017-2 《Java程序设计》第七周学习总结
教材学习内容总结 时间度量 格林尼治标准时间(GMT):现在GMT已不作为标准时间使用. 世界时(UT):借助观测远方星体跨过子午线而得,受地球自转速度影响. 国际原子时(TAI):铯原子辐射振动幅度 ...
- hdu 5184 类卡特兰数+逆元
BC # 32 1003 题意:定义了括号的合法排列方式,给出一个排列的前一段,问能组成多少种合法的排列. 这道题和鹏神研究卡特兰数的推导和在这题中的结论式的推导: 首先就是如何理解从题意演变到卡特兰 ...
- (转)C++初始化与赋值
来源:http://www.cnblogs.com/chio/archive/2008/10/06/1305145.html 先来个区别说明:赋值操作是在两个已经存在的对象间进行的,而初始化是要创建一 ...
- 【socket编程】什么是socket编程
Socket简介 Socket是进程通讯的一种方式,即调用这个网络库的一些API函数实现分布在不同主机的相关进程之间的数据交换. 几个定义: (1)IP地址:即依照TCP/IP协议分配给本地主机的网络 ...
- LG2521 [HAOI2011]防线修建
题意 题目描述 近来A国和B国的矛盾激化,为了预防不测,A国准备修建一条长长的防线,当然修建防线的话,肯定要把需要保护的城市修在防线内部了.可是A国上层现在还犹豫不决,到底该把哪些城市作为保护对象呢? ...
- shell学习笔记汇总
1.shell脚本中函数使用 函数定义在前,调用在后,顺序反了就没有效果了.函数调用为:函数名 参数列表 函数内部通过以下变量访问函数的参数:shell脚本函数中: $0: 这个脚本的名字 $n: 这 ...
- TensorFlow笔记-05-反向传播,搭建神经网络的八股
TensorFlow笔记-05-反向传播,搭建神经网络的八股 反向传播 反向传播: 训练模型参数,在所有参数上用梯度下降,使用神经网络模型在训练数据上的损失函数最小 损失函数:(loss) 计算得到的 ...
- SOALog
项目地址 : https://github.com/kelin-xycs/SOALog SOALog 为 SOA 架构 提供一种 松耦合 乐观 的 数据一致性 解决方案,说白了这个组件的功能就是 记 ...
- python之 数据类型判定与类型转换
一. 判断数据类型 0.type(x)type()可以接收任何东西作为参数――并返回它的数据类型.整型.字符串.列表.字典.元组.函数.类.模块,甚至类型对象都可以作为参数被 type 函数接受. & ...
- C# 正则表达式入门
转自:http://www.cnblogs.com/KissKnife/archive/2008/03/23/1118423.html 另外推荐一篇学正则表达式的文章:http://www.unibe ...