上文提到对从深度图生成点云,需要对每一个像素点进行转换。如果要对很多图像进行深度和点云的转换,在cpu中是极其耗费时间的,因此这里介绍使用cuda在gpu中进行深度点云转换。
pycuda的安装自行百度,这里只介绍使用。
1.首先构造cuda程序,这里可以看到,pycuda使用时候主题还是c语言,在python中通过字符串的形式表示。
import pycuda.autoinit
from pycuda.compiler import SourceModule
import pycuda.gpuarray as gpuarray
#-----cuda程序------
mod = SourceModule(r"""
void __global__ get_pcd(const float image[480][640][3], const float depth[480][640],float xyz[480][640][3],float rgb[480][640][3])
{
const int gpu_m = blockIdx.x*blockDim.x+threadIdx.x;
const int gpu_n = blockIdx.y*blockDim.y+threadIdx.y;
float fx_d = 5.8262448167737955e+02;
float fy_d = 5.8269103270988637e+02;
float cx_d = 3.1304475870804731e+02;
float cy_d = 2.3844389626620386e+02;
xyz[gpu_m][gpu_n][2] = depth[gpu_m][gpu_n];
xyz[gpu_m][gpu_n][0] = (gpu_n - cx_d) * depth[gpu_m][gpu_n] / fx_d;
xyz[gpu_m][gpu_n][1] = (gpu_m - cy_d) * depth[gpu_m][gpu_n] / fy_d;
rgb[gpu_m][gpu_n][0] = image[gpu_m][gpu_n][0]/255 * 255;
rgb[gpu_m][gpu_n][1] = image[gpu_m][gpu_n][1]/255 * 255;
rgb[gpu_m][gpu_n][2] = image[gpu_m][gpu_n][2]/255 * 255;
}""")
get_pcd = mod.get_function("get_pcd")
上面代码中,最重要的两句如下:
const int gpu_m = blockIdx.x*blockDim.x+threadIdx.x;
const int gpu_n = blockIdx.y*blockDim.y+threadIdx.y;
简单理解,gpu中有“块”和“线程”的概念,blockIdx代表当前是第几块,blockDim代表每个块有几个线程,threadIdx是第几块下的第几个线程。(比如我们在学校里要找第231号,如果一个班级有50个人(blockDim),则第231号是5班(blockIdx)第31号(threadIdx))。而对于图像,分为x和y两个方向。
2.参数传入gpu
A = np.array(range(1, 921601)).reshape((480, 640, 3))
B = np.array(range(1, 307201)).reshape((480, 640, 1))
#depth_GPU = gpuarray.to_gpu(depth.astype(np.float32))
#image_GPU = gpuarray.to_gpu(image.astype(np.float32))
depth_GPU = gpuarray.to_gpu(A.astype(np.float32))
image_GPU = gpuarray.to_gpu(B.astype(np.float32))
xyz_gpu = gpuarray.to_gpu(A.astype(np.float32))
rgb_gpu = gpuarray.to_gpu(A.astype(np.float32))
在构造cuda函数的时候,c语言函数有四个参数传入,分别是彩色图、深度图像、点云的位置和点云的色彩。因此传入gpu的参数需要与这对应,注意维度。A、B两个矩阵是随机产生的,分别认为是彩色图和深度图,而xyz和rgb是在cuda运算过程中记录结果的矩阵,这里将其传进去感觉只是为了占内存(数值内容无所谓,但是维度要对)。
3. gpu中cuda运算及结果
get_pcd(image_GPU, depth_GPU, xyz_gpu, rgb_gpu, grid=(48,64,1),block=(10,10,1))
rgb = rgb_gpu.get()
xyz = xyz_gpu.get()
xyz = np.array(xyz)
rgb = np.array(rgb)
之前构造了get_pcd函数,这里开始调用get_pcd。如图所示,除了四个参数传递意外,还规定了grid和block。这个就是之前提到的块和线程。对于x方向,有48个块,每个块10个线程。对于y方向,64个块,每个块10个线程,z方向1个块1个线程。这个部分对应了const int gpu_m = blockIdx.x*blockDim.x+threadIdx.x; const int gpu_n = blockIdx.y*blockDim.y+threadIdx.y; 需要注意的是,我在使用过程中发现,块和线程设置少了,剩下部分不会进行运算;而如果块和线程设置多了grid=(100,100,1), block=(10,10,1),计算的结果也不正确。因此我在使用过程中,设置了块和线程数好刚对应图像维度参数。
接下来的函数就是将结果从gpu中取出来,并转换为numpy的形式。
实际过程中,感觉在480*640图像的点云计算上,pycuda并行计算比单cpu跑快30倍(50倍)以上!