pycuda实现图像灰度化

195 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第23天,点击查看活动详情

图像的灰度化已经不难实现,opencv、PIL等库函数都有实现,直接调用现成的包即可,但是当我们使用pycuda尝试对图像等方面进行操作的时候,用这个来练手,增加一下对于pycuda的学习还是不错的,前面我们先从标量开始,然后逐步深入的到向量,最近一篇博客终于涉及了对于二维矩阵的操作,一个例子拿来练手总归是少了一些,本博客通过读取外部RGB三通道图像,通过使用pycuda编写核函数进行图像的灰度化操作,当然灰度化这部分涉及一个公式,就是RGB图像三通道乘以某个系数进行求和转换为单通道的公式,非常简单,公式如下

gray = 0.21 *R + 0.71*G + 0.07 * B

下面看一下核函数,应该如何设计

kernel = """
__global__ void bw( float *inIm, int check ){

    int idx = (threadIdx.x ) + blockDim.x * blockIdx.x ;
    if(idx *3 < check*3)
    {
       int val = 0.21 *inIm[idx*3] + 0.71*inIm[idx*3+1] + 0.07 * inIm[idx*3+2];
       inIm[idx*3]= val;
       inIm[idx*3+1]= val;
       inIm[idx*3+2]= val;
    }
}
"""

然后就是对核函数的调用,本博客的核函数写的稍微与上一篇博客有点区别,SourceModule的位置发生了变化,说明SourceModule的用法比较灵活,位置不固定,而且核函数也可以单独写到一个文件中,通过读取外部文件的形式进行调用。

im = Image.open(inPath)
px = numpy.array(im)
px = px.astype(numpy.float32)
d_px = cuda.mem_alloc(px.nbytes)
# 主机拷贝到GPU函数
cuda.memcpy_htod(d_px, px)
BLOCK_SIZE = 1024
block = (1024, 1, 1)
checkSize = numpy.int32(im.size[0] * im.size[1])
grid = (int(im.size[0] * im.size[1] / BLOCK_SIZE) + 1, 1, 1)
mod = SourceModule(kernel)
func = mod.get_function("bw")
# 主要是这一句
func(d_px, checkSize, block=block, grid=grid)
bwPx = numpy.empty_like(px)
cuda.memcpy_dtoh(bwPx, d_px)
bwPx = (numpy.uint8(bwPx))
pil_im = Image.fromarray(bwPx, mode="RGB")
pil_im.save(outPath)

上述代码的基本含义就是先通过Image读取一幅图像,然后转化为numpy格式,将数据类型转化为float32类型,在主机上开辟一块与px大小一样的内存,然后将px从cpu复制到GPU开辟的内存上(这里也可以使用gpuarray.empty_like来替代开辟内存和拷贝两行代码,这一行就能干两行的活,但是有必要换个形式学习一下。) 再往下就是block和grid的计算上一篇博客已经详细的叙述过,本博客不再赘述,不会的可以回到上一篇博客看介绍。

checksize就是变为灰度图的大小。后面bwpx就是在GPU上新建一个拷贝,然后使用memcpy_dtoh从GPU拷贝回主机,与前面的函数不一样,字母顺序不一样,注意别看错。最后就是从numpy转化为Image,然后存储就完事了。

上述代码调用如下

CudaBlackWhite('cat.jpg', 'gray_cat.jpg')

前面一个cat表示原图像,后面表示处理后保存的图像名。结果如下

cat.jpg

gray_cat.jpg

没啥大的变化。就是彩色转灰度,主要是再学习一下直接对图像的操作。