OpenCL 学习step by step (6) 旋转图像

简介: 在本教程中,我们学习用opencl进行简单的图像处理,对一个图片进行旋转。图片读入、保存等工作,我们使用开源的FreeImage,下载地址:http://freeimage.sourceforge.net/       首先我们建立一个gFreeImage类,用来装入图像,该类主要调用FreeImage的函数,首先会初始化FreeImage库,然后根据文件名猜测图像文件格式,最终load图像文件到变量FIBITMAP *bitmap中去。

      在本教程中,我们学习用opencl进行简单的图像处理,对一个图片进行旋转。图片读入、保存等工作,我们使用开源的FreeImage,下载地址:http://freeimage.sourceforge.net/

      首先我们建立一个gFreeImage类,用来装入图像,该类主要调用FreeImage的函数,首先会初始化FreeImage库,然后根据文件名猜测图像文件格式,最终load图像文件到变量FIBITMAP *bitmap中去。同时,我们还定义了2个缓冲

unsigned char *imageData;
unsigned char *imageData4;

用来存放图像数据,之所以定义imageData4,是因为通常的图片文件,比如jpg,bmp都是3个通道,没有包括alpha通道,但是在gpu中处理数据时候,通常以vector4或者vector的形式进行,不能以vector3进行,所以我们装入图像后,会把imageData指向图像数据,同时生成包括alpha通道的图像数据imageData4。

     另外,我们还定义了一个函数LoadImageGrey,该函数用来装入灰度图,灰度图一个像素用一个uchar表示。

在main.cpp中,我们首先定义一个cpu处理图像旋转的函数:

//CPU旋转图像
void cpu_rotate(unsigned char* inbuf, unsigned char* outbuf, int w, int h,float sinTheta, float cosTheta)
    {
    int i, j;
    int xc = w/2;
    int yc = h/2;

    for(i = 0; i < h; i++)
        {
        for(j=0; j< w; j++)
            {
            int xpos =  ( j-xc)*cosTheta - (i-yc)*sinTheta+xc;   
            int ypos =  (j-xc)*sinTheta + ( i-yc)*cosTheta+yc;

            if(xpos>=0&&ypos>=0&&xpos<w&&ypos<h)
                outbuf[ypos*w + xpos] = inbuf[i*w+j];
            }
        }
    }

    在main函数中,我们首先会装入图像文件,代码如下:

int W, H;
gFreeImage img;
if(!img.LoadImageGrey("lenna.jpg"))
{
printf("can‘t load lenna.jpg\n");
exit(0);
}
else
src_image = img.getImageDataGrey(W, H);

size_t mem_size = W*H;
cpu_image = (unsigned char*)malloc(mem_size);

    之后,定义2个cl memory对象,一个用来放原始图像,一个用来放旋转后的图像。

//创建2个OpenCL内存对象
cl_mem d_ip = clCreateBuffer(
    context, CL_MEM_READ_ONLY,
    mem_size,
    NULL, NULL);
cl_mem d_op = clCreateBuffer(
    context, CL_MEM_WRITE_ONLY,
    mem_size,
    NULL, NULL);

cl_event writeEvt;
status = clEnqueueWriteBuffer (   
    queue , d_ip, CL_TRUE,
    0, mem_size, (void *)src_image,
    0, NULL, &writeEvt);
//等待数据传输完成再继续往下执行
status = clFlush(queue);
waitForEventAndRelease(&writeEvt);
//clWaitForEvents(1, &writeEvt);

   旋转kernel函数需要传入6个参数:

//创建Kernel对象
cl_kernel kernel = clCreateKernel( program, "image_rotate", NULL );
//设置Kernel参数
float sintheta = 1, costheta = 0;
clSetKernelArg(kernel, 0, sizeof(cl_mem),  (void *)&d_ip);
clSetKernelArg(kernel, 1, sizeof(cl_mem),  (void *)&d_op);
clSetKernelArg(kernel, 2, sizeof(cl_int),  (void *)&W);
clSetKernelArg(kernel, 3, sizeof(cl_int),  (void *)&H);
clSetKernelArg(kernel, 4, sizeof(cl_float), (void *)&sintheta);
clSetKernelArg(kernel, 5, sizeof(cl_float), (void *)&costheta);

kernel执行的代码为:

//执行kernel,Range用2维,work itmes size为W*H,
cl_event ev;
size_t globalThreads[] = {W, H};
size_t localThreads[] = {16, 16}; // localx*localy应该是64的倍数
printf("global_work_size =(%d,%d), local_work_size=(16, 16)\n",W,H);

clTimer.Reset();
clTimer.Start();
clEnqueueNDRangeKernel( queue,
    kernel,
    2,
    NULL,
    globalThreads,
    localThreads, 0, NULL, &ev);
//没有设置local group size时候,系统将会自动设置为 (256,1)
status = clFlush( queue );
waitForEventAndRelease(&ev);
//clWaitForEvents(1, &ev);

clTimer.Stop();
printf("kernal total time:%.6f ms \n ", clTimer.GetElapsedTime()*1000 );

kernel函数代码为:

#pragma OPENCL EXTENSION cl_amd_printf : enable
__kernel void image_rotate( __global uchar * src_data, __global uchar * dest_data, //源图像和输出图像都放在global memory中
int W, int H, //图像size
float sinTheta, float cosTheta ) //旋转角度
{
const int ix = get_global_id(0);
const int iy = get_global_id(1);

int xc = W/2;
int yc = H/2;

int xpos = ( ix-xc)*cosTheta - (iy-yc)*sinTheta+xc;
int ypos = (ix-xc)*sinTheta + ( iy-yc)*cosTheta+yc;

if ((xpos>=0) && (xpos< W) && (ypos>=0) && (ypos< H)) //边界检测
{
dest_data[ypos*W+xpos]= src_data[iy*W+ix];
}
}


gpu执行完毕后,旋转后的图像保存在lenna_rotate.jpg,我们还会用cpu rotate函数执行一次旋转,同时把生成的图像保存到cpu_lenna_rotate.jpg。

完整的代码请参考:

工程文件gclTutorial5

代码下载:

http://files.cnblogs.com/mikewolf2002/gclTutorial.zip

相关文章
|
消息中间件 存储 Java
《RabbitMQ》| 解决消息延迟和堆积问题
本文主要介绍 RabbitMQ的常见问题
1170 1
|
4月前
|
机器学习/深度学习 监控 算法
摊位货摊自动识别与监控系统识别系统开箱即用教程 (YOLOv8)| 完整源码与部署教程
本项目展示了如何通过 YOLOv8 深度学习模型与 PyQt5 图形界面结合,开发一个 摊位货摊自动识别与监控系统。该系统能够高效地检测摊位上的商品,并对周围的行为进行实时监控,为摊位管理带来极大的便利。系统支持多种输入方式,如图像、视频和摄像头,并具备异常行为检测和报警功能。
摊位货摊自动识别与监控系统识别系统开箱即用教程 (YOLOv8)| 完整源码与部署教程
|
存储 编译器 C++
【C++从0到王者】第三十五站:面试官让手撕红黑树,我直接向他秀一手手撕map与set
【C++从0到王者】第三十五站:面试官让手撕红黑树,我直接向他秀一手手撕map与set
213 0
|
Web App开发 开发工具 Android开发
【Flutter】Flutter安装和配置(mac)
【Flutter】Flutter安装和配置(mac)
|
Java 数据库连接 数据库
从入门到精通---深入剖析Spring DAO
在Java企业级开发中,Spring框架以其强大的功能和灵活性,成为众多开发者的首选。Spring DAO(Data Access Object)作为Spring框架中处理数据访问的重要模块,对JDBC进行了抽象封装,极大地简化了数据访问异常的处理,并能统一管理JDBC事务。本文将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring DAO,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
227 1
|
存储 Docker 容器
docker中挂载现有目录作为数据卷
【10月更文挑战第14天】
248 1
|
传感器 人工智能 算法
探索人工智能与物联网的融合创新
随着科技的迅猛发展,人工智能(AI)和物联网(IoT)这两大技术正在逐渐深度融合,催生出一系列创新性应用。本文将探讨这一领域的技术原理、应用场景及其未来发展前景,旨在为读者提供全面而深入的理解。
1015 2
|
机器学习/深度学习 传感器 算法
【机器学习】在聚类算法中,使用曼哈顿距离和使用欧式距离有什么区别?
【5月更文挑战第12天】【机器学习】在聚类算法中,使用曼哈顿距离和使用欧式距离有什么区别?
|
传感器 运维 机器人
matlab实现扩展卡尔曼滤波(EKF)进行故障检测
matlab实现扩展卡尔曼滤波(EKF)进行故障检测
|
机器学习/深度学习 API vr&ar
Qt, OpenCV与OpenGL协同作战:图像处理与三维图形界面的完美结合
Qt, OpenCV与OpenGL协同作战:图像处理与三维图形界面的完美结合
2125 5

热门文章

最新文章