I am trying to apply an undistortion map (in X and Y direction) to an image using textures. The input image I use is in unsigned char. I also want my output type to be unsigned char.
Now I have a program in MATLAB doing the interpolation in float which leads to a good result. I am able to recreate the interpolation in C++/CUDA in unsigned char, but my output image has some areas which are a bit more pixelated compared to the MATLAB approach. This is the code I use in C++:
__global__ void undistortionKernel(uchar4* outputImage, size_t pitchOutputImage,
cudaTextureObject_t inputImageTex,
const float* XundistMap, size_t XpitchUndistmap,
const float* YundistMap, size_t YpitchUndistmap,
int width, int height)
{
const int x = blockIdx.x * blockDim.x + threadIdx.x;
const int y = blockDim.y * blockIdx.y + threadIdx.y;
const float tx = (XundistMap[y * XpitchUndistmap + x]);
const float ty = (YundistMap[y * YpitchUndistmap + x]);
if (x >= width || y >= height) return;
uchar4 outputImageTemp = tex2D<uchar4>(inputImageTex, tx, ty);
outputImage[y * pitchOutputImage + x] = outputImageTemp;
}
void undistortImage(unsigned char* outputImage, size_t pitchOutputImage, unsigned char* inputImage, size_t pitchInputImage, const float* undistMapX, size_t pitchUndistmapX, const float* undistMapY, size_t pitchUndistmapY, int width, int height, cudaStream_t stream)
{
cudaResourceDesc resDesc = {};
resDesc.resType = cudaResourceTypePitch2D;
resDesc.res.pitch2D.width = width;
resDesc.res.pitch2D.height = height;
resDesc.res.pitch2D.devPtr = inputImage;
resDesc.res.pitch2D.pitchInBytes = pitchInputImage;
resDesc.res.pitch2D.desc = cudaCreateChannelDesc(8, 8, 8, 8, cudaChannelFormatKindUnsigned);
cudaTextureDesc texDesc = {};
texDesc.readMode = cudaReadModeElementType;
texDesc.addressMode[0] = cudaAddressModeClamp;
texDesc.addressMode[1] = cudaAddressModeClamp;
cudaTextureObject_t inputImageTex;
CUDA_CHECK(cudaCreateTextureObject(&inputImageTex, &resDesc, &texDesc, 0));
dim3 block(32, 8);
dim3 grid = paddedGrid(block.x, block.y, width, height);
undistortionKernel << <grid, block, 0, stream >> >
(reinterpret_cast<uchar4*>(outputImage),
pitchOutputImage / sizeof(uchar4),
inputImageTex,
undistMapX, pitchUndistmapX / sizeof(float),
undistMapY, pitchUndistmapY / sizeof(float),
width, height);
CUDA_CHECK(cudaDestroyTextureObject(inputImageTex));
}
Now I tried to adapt my code to doing the interpolation with float (the changes in the kernel should be obvious, in the function calling the kernel I changed to using “cudaChannelFormatKindFloat”), but my output image is black. My error check also tells me this: “Cuda Error: invalid channel descriptor” when using cudaCreateTextureObject. This is the code:
__device__ unsigned char float2uchar(float floatin)
{
unsigned char char_out;
int int_temp = __float2uint_rn(floatin);
char_out = (char)int_temp;
return char_out;
}
__global__ void undistortionKernel(uchar4* outputImage, size_t pitchOutputImage,
cudaTextureObject_t inputImageTex,
const float* XundistMap, size_t XpitchUndistmap,
const float* YundistMap, size_t YpitchUndistmap,
int width, int height)
{
const int x = blockIdx.x * blockDim.x + threadIdx.x;
const int y = blockDim.y * blockIdx.y + threadIdx.y;
const float tx = (XundistMap[y * XpitchUndistmap + x]);
const float ty = (YundistMap[y * YpitchUndistmap + x]);
if (x >= width || y >= height) return;
float4 outputImageTempFloat4 = tex2D<float4>(inputImageTex, tx, ty);
uchar4 outputImageTemp;
outputImageTemp.x = float2uchar(outputImageTempFloat4.x);
outputImageTemp.y = float2uchar(outputImageTempFloat4.y);
outputImageTemp.z = float2uchar(outputImageTempFloat4.z);
outputImageTemp.w = float2uchar(outputImageTempFloat4.w);
outputImage[y * pitchOutputImage + x] = outputImageTemp;
}
void undistortImage(unsigned char* outputImage, size_t pitchOutputImage, unsigned char* inputImage, size_t pitchInputImage, const float* undistMapX, size_t pitchUndistmapX, const float* undistMapY, size_t pitchUndistmapY, int width, int height, cudaStream_t stream)
{
cudaResourceDesc resDesc = {};
resDesc.resType = cudaResourceTypePitch2D;
resDesc.res.pitch2D.width = width;
resDesc.res.pitch2D.height = height;
resDesc.res.pitch2D.devPtr = inputImage;
resDesc.res.pitch2D.pitchInBytes = pitchInputImage;
resDesc.res.pitch2D.desc = cudaCreateChannelDesc(8, 8, 8, 8, cudaChannelFormatKindFloat);
cudaTextureDesc texDesc = {};
texDesc.readMode = cudaReadModeElementType;
texDesc.addressMode[0] = cudaAddressModeClamp;
texDesc.addressMode[1] = cudaAddressModeClamp;
texDesc.filterMode = cudaFilterModeLinear;
cudaTextureObject_t inputImageTex;
CUDA_CHECK(cudaCreateTextureObject(&inputImageTex, &resDesc, &texDesc, 0));
dim3 block(32, 8);
dim3 grid = paddedGrid(block.x, block.y, width, height);
undistortionKernel << <grid, block, 0, stream >> >
(reinterpret_cast<uchar4*>(outputImage),
pitchOutputImage / sizeof(uchar4),
inputImageTex,
undistMapX, pitchUndistmapX / sizeof(float),
undistMapY, pitchUndistmapY / sizeof(float),
width, height);
CUDA_CHECK(cudaDestroyTextureObject(inputImageTex));
}
I am not sure how to load an unsigned char array to a float texture. Any hints and advices are very much appreciated :)