CUDA:Texture 图像应用详解

CUDA:Texture 图像应用详解

什么时候使用texture

原则:
1.当对图片内存的读取效率不高时,即(memory reads do not follow the access patterns)
2.果涉及到插值,对影像数据的处理使用Texture。
第一种情况真的很少,因为现在缓存的颗粒都是32,所以在一般情况下,全局内存访问的效率都比较高。这时就没有必要使用texture了
第二种情况:有插值需要时,可以选择使用texture,因为在二维图像中,插值一个像素,需要周围的4个像素的值,所以需要取4个全局变量,而如果是texture插值一个像素,只需要取一个像素值。所以有插值需要的应用使用texture是很不错的。

使用texture的一般步骤:

  1. 定义纹理参考Texture Reference
  2. 声明CUDA数组
  3. 拷贝主机内存到CUDA数组
  4. 设置纹理参数(模式)
  5. 绑定纹理
  6. Kernel中取像素值改为Texture fetch
  7. 解除绑定
  8. 释放CUDA数组

上面提到有插值需要时,使用texture是可行的。但不是所有插值都会有好的效果,为什么呢,下面详解:


处理图像插值问题的几种情况

  1. 处理灰度图像插值问题
  2. 处理三通道影像数据,每通道是usigned char的情况

1.处理灰度图像插值问题
处理处理灰度图像插值问题,一般情况下都会有较高的提升,比较推荐这种情况使用texture。
使用细节注意:
灰度值插值,一般的灰度的像素值都是float的,按照上面的使用texture的一般步骤,
即可。
2.处理三通道影像数据,每通道是usigned char的情况

如果是轻量级计算问题(读取像素需要较少)不推荐,如果是显示问题或大量读取像素的问题(相对概念,小图像多多次是,大图像读一次不是)推荐使用texture。
为什么?:
因为图像像素的获取方式不同:
因为在图像处理领域,在默认的影像中像素的获取方式是u[x][y][z]=u[y*width*depth+x*depth+z],如opencv,gdal的数据都是这么存放的
但在texture中,tex3D(texref,x,y,z)中读取数据的是u[x][y][z]=u[x+y*pitch+z*pitch*height];(不懂pitch的可以先把pitch看成width)

为了正确的读取像素的值,需要做一次数据转换:

void changeImgData(ImageData& img)
{
    //改变数据的访问方式,在默认的影像中像素的获取方式是u[x][y][z]=u[y*width*depth+x*depth+z],如opencv,gdal的数据都是这么存放的
    //但在texture中,tex3D(texref,x,y,z)中读取数据的是u[x][y][z]=u[x+y*pitch+z*pitch*height];(不懂的可以先把pitch看成width)
    int width = img.col;
    int height = img.row;
    int depth = img.nBand;
    DT_8U* dataOld = img.data;
    DT_8U* dataNew = new DT_8U[width*height*depth];
    for (int z = 0; z < depth; z++)
        for (int y = 0; y < height; y++)
            for (int x = 0; x < width; x++)
            {
                dataNew[x + y*width + z*width*height] = dataOld[y*width*depth + x*depth + z];
            }
    delete img.data;
    img.data = dataNew;
}

正因为计算消耗时间,所以即使texture对核函数有提速,但总耗时不一定减少。
但对于OpenGL等显示的应用,数据初始化和拷贝一次,后面的计算都是在GPU上的,所以推荐使用,但对于轻量级计算问题,要斟酌,不一定有好的效果。

有同学会说我不想改变数据的存储方式,我通过找到两种计算式的映射关系,来解决数据读取问题。
这种思路我也考虑过:
u[y*width*depth+x*depth+z]= u[x1+y1*pitch+z1*pitch*height]
这个式子在xyz和x1,y1,z1都是整数的时候可解,可以解决取值问题,但我们上面讨论了,纹理是在使用插值时会有好的效果,即x,y,z和x1,y1,z1都是小数,顶多z,z1是整数,但这还是无解的。所以找不到映射关系解决插值问题,不插值使用texture也就没有必要了。

使用细节注意:

  1. 三维的RGB可以有两种方式
    使用texture laryer或使用tex3D,他们的不同在我看来除了使用的x,y,z大小限制不同外,也就是z是否是整数的不同,texture laryer要求z是整数,tex3D不要求,可以在三维上进行三线性插值。他们的要求的数据存储方式,和读取方式都是相同的,因为他们都复制自3D的CUDA Array

  2. 纹理参数(模式)的选择
    由于原数据是RGB,每一通道是8位的,所以texture的类型一定是unsigned char。但插值需要返回float的类型,所以这时的read mode必须选择cudaReadModeNormalizedFloat。即计算归一化的结果。但需要在结果上多乘以255(因为像素范围是[0,255))。

  3. Z值的选择
    如果使用的是tex3D,那么为保证z值是整数,需要z = z+0.5.
    texture在插值的时候,会将x,y,z默认-0.5,以匹配其插值的算法,第三维,我们是不想让它插值,所以先+0.5f就能得到第三维在0,1,2的结果。即z轴不插值的结果。
    具体见cuda programming guide的texture fetch的插值部分,或者见CUDA:Texture所需参数详解

由于,代码是项目中的我就不发了,实例见cuda sample中的关于texture的部分。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值