使用OpenCL+OpenCV实现图像卷积(一)

来源:互联网 发布:番茄时间管理 知乎 编辑:程序博客网 时间:2024/05/29 17:46

[题外话]近期申请了一个微信公众号:平凡程式人生。有兴趣的朋友可以关注,那里将会涉及更多更新OpenCL+OpenCV以及图像处理方面的文章。

1、图像卷积的原理

图像卷积在图像处理中十分常见。对一副二维数字图像进行卷积,其意味着:源图像作为输入数据,利用卷积操作数进行卷积操作,卷积之后的图像数据作为输出图像。卷积操作数称为卷积核,它是一个大小固定、由数值参数构成的数组。该数组的参考点通常位于数组的中心,数组的大小称为核支撑。

卷积运算是加权求和的过程,使用到的图像区域中的每个像素分别与卷积和(权矩阵)的每个元素对应相乘,所有乘积之和作为区域中心像素的新值。

卷积示例:3 * 3的像素区域R与卷积核G的卷积运算:

R5(中心像素)=R1G1 +R2G2 + R3G3 + R4G4 + R5G5 + R6G6 + R7G7 + R8G8 + R9G9


如果对一幅图像进行卷积运算,可利用以数组为中心为参考点的3*3卷积核。首先将核的参考点定位于图像的第一个像素点,核的其余元素覆盖图像总其对应的局部像素点。对于每一个核点,我们可以得到这个点的值以及图像中对应图像点的值。将这些值相乘并求和,并将这个结果放在与输入图像参考点所对应的位置。通过在整个图像上扫描卷积核,对图像的每个点重复此操作。最终可以得到图像的卷积图像。

当然,我们可以利用方程表示这个过程,定义图像为I(x,y),核为G(i,y)(其中0<i<Mi-1,0<j<Mj-1) 参考点位于(ai,aj)坐标上,则卷积H(x,y)定义如下:

H(x,y) = sum[I(x+i-ai,y+j-aj)G(i,j)].

2、常用卷积核

在图像卷积中,常用的卷积核有如下六种:低通滤波器、高通滤波器、平移和差分边缘检测、匹配滤波边缘检测、边缘检测、梯度方向边缘检测。

六种卷积核的常用示例如下图所示:

 

 

  

3、程序设计

在这个程序中,我们使用一个3 * 3的边缘检测卷积核对灰度图像进行卷积运算。大致的处理流程如下:

1>     调用OpenCVAPI imread()读取一张彩色JPEG图片,将它存储在MAT变量中。该变量的data成员中存储着将JPEG图片解码后的RGB数据。

2>     调用OpenCVAPI cvtColor()将存储RGB数据的MAT变量转换为只存储灰度图像数据的MAT对象。也可以使用函数imread()时直接将JPEG图像解码转换为灰度图像。

3>     MAT对象的成员width和height存储着解码后图像的分辨率信息。根据当前分辨率,分配处理图像时所用的输入buffer和输出buffer。它们都按照存储char型数据进行空间申请。

4>     将MAT对象的成员data中数据copy到输入buffer中。同时将输出buffer初始化为全0。到此,我们调用OpenCV的API所要做的事情告一段落了。接下来就要调用OpenCL的API做事情了。

5>     调用OpenCLAPI clGetPlatformIDs()直接获取第一个可用的平台信息。该函数一般是先用它获取支持OpenCL平台的数目,然后再次调用它获取某个平台的信息。两次调用,通过传递不同参数区分。

使用下面的code获取支持OpenCL的platform数目:

ciErrNum= clGetPlatformIDs(0, NULL, &numPlatform);

然后申请相应的空间,并获取所有platform的信息:

platforms= (cl_platform_id *)malloc(numPlatform * sizeof(cl_platform_id));

  ciErrNum = clGetPlatformIDs(numPlatform,platforms, NULL);

另外,我们调用函数clGetPlatformInfo()对各个platform的信息进行了获取并显示。

6>     调用OpenCLAPI clGetDeviceIDs()获取支持OpenCL1.2版本的可用的设备。同样,这个函数也可以调用两次,分别获取当前平台的设备数目,再获取某个设备信息。

7>     调用OpenCL API clCreateContext()创建上下文。

8>     调用OpenCL API clCreateCommandQueue()创建host与device之间交互的command队列。

9>     调用OpenCL API clCreateImage()在设备端分配存储图像的image buffer。在OpenCL 1.0/1.1中支持接口clCreateImage 2D(),而OpenCL1.2中支持接口clCreateImage (),代替了之前的接口clCreateImage 2D()。

10> 调用OpenCLAPI clCreateBuffer()创建存储卷积核filter的buffer。

11> 调用OpenCL APIclEnqueueWriteImage ()将之前存储灰度图像数据的输入buffer内存copy到设备端buffer中。

12> 调用OpenCL APIclEnqueueWriteBuffer()将存储卷积核数据的数组内容copy到设备端buffer中。

13> 调用文件读取函数,将kernel文件ImageRotate.cl中的内容读取到string变量中。

14> 调用OpenCL APIclCreateProgramWithSource(),使用kernel的源码创建program对象。

15> 调用OpenCL APIclBuildProgram()编译program对象。

16> 调用OpenCL APIclCreateKernel(),使用编译完的程序对象创建kernel。

17> 调用OpenCL APIclSetKernelArg()为kernel程序传递参数,包括输入输出buffer地址,图像分辨率和sin()\cos()值。

18> 调用OpenCL APIclEnqueueNDRangeKernel()执行kernel。

19> 调用OpenCL API clEnqueueReadImage()将设备端处理完的图像数据copy到host端的输出buffer中。

20> 将输出buffer中的数据copy到MAT对象的成员data中。

21> 调用OpenCV APIimwrite()将旋转后的灰度图像保存到文件中,编码为JPEG保存起来。

22> 释放输入输出buffer空间,释放OpenCL创建的各个对象。

(未完待续)

原创粉丝点击