Files
fold_slice/+astra/ASTRA_GPU_wrapper/util3d.cu
T
2026-08-07 15:56:42 +09:00

688 lines
17 KiB
Plaintext

/*
-----------------------------------------------------------------------
Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp
2014-2015, CWI, Amsterdam
Contact: astra@uantwerpen.be
Website: http://sf.net/projects/astra-toolbox
This file is part of the ASTRA Toolbox.
The ASTRA Toolbox is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The ASTRA Toolbox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
-----------------------------------------------------------------------
$Id$
*/
#include <cstdio>
#include <cassert>
#include "util3d.h"
#include <ctime>
#include <cuda.h>
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
//#include "../2d/util.h"
#include "astra/Logging.h"
#include "mex.h"
namespace astraCUDA3d {
cudaPitchedPtr allocateVolumeData(const SDimensions3D& dims)
{
cudaExtent extentV;
extentV.width = dims.iVolX*sizeof(float);
extentV.height = dims.iVolY;
extentV.depth = dims.iVolZ;
cudaPitchedPtr volData;
cudaError err = cudaMalloc3D(&volData, extentV);
if (err != cudaSuccess) {
astraCUDA3d::reportCudaError(err);
ASTRA_ERROR("Failed to allocate %dx%dx%d GPU buffer", dims.iVolX, dims.iVolY, dims.iVolZ);
volData.ptr = 0;
// TODO: return 0 somehow?
}
return volData;
}
cudaPitchedPtr allocateProjectionData(const SDimensions3D& dims)
{
cudaExtent extentP;
extentP.width = dims.iProjU*sizeof(float);
extentP.height = dims.iProjAngles;
extentP.depth = dims.iProjV;
cudaPitchedPtr projData;
cudaError err = cudaMalloc3D(&projData, extentP);
if (err != cudaSuccess) {
mexPrintf("Failed to allocate %dx%dx%d GPU buffer", dims.iProjU, dims.iProjAngles, dims.iProjV);
projData.ptr = 0;
// TODO: return 0 somehow?
}
return projData;
}
bool zeroVolumeData(cudaPitchedPtr& D_data, const SDimensions3D& dims)
{
char* t = (char*)D_data.ptr;
cudaError err;
for (unsigned int z = 0; z < dims.iVolZ; ++z) {
err = cudaMemset2D(t, D_data.pitch, 0, dims.iVolX*sizeof(float), dims.iVolY);
ASTRA_CUDA_ASSERT(err);
t += D_data.pitch * dims.iVolY;
}
return true;
}
bool zeroProjectionData(cudaPitchedPtr& D_data, const SDimensions3D& dims)
{
char* t = (char*)D_data.ptr;
cudaError err;
for (unsigned int z = 0; z < dims.iProjV; ++z) {
err = cudaMemset2D(t, D_data.pitch, 0, dims.iProjU*sizeof(float), dims.iProjAngles);
ASTRA_CUDA_ASSERT(err);
t += D_data.pitch * dims.iProjAngles;
}
return true;
}
bool copyVolumeToDevice(const float* data, cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch)
{
if (!pitch)
pitch = dims.iVolX;
cudaPitchedPtr ptr;
ptr.ptr = (void*)data; // const cast away
ptr.pitch = pitch*sizeof(float);
ptr.xsize = dims.iVolX*sizeof(float);
ptr.ysize = dims.iVolY;
cudaExtent extentV;
extentV.width = dims.iVolX*sizeof(float);
extentV.height = dims.iVolY;
extentV.depth = dims.iVolZ;
cudaPos zp = { 0, 0, 0 };
cudaMemcpy3DParms p;
p.srcArray = 0;
p.srcPos = zp;
p.srcPtr = ptr;
p.dstArray = 0;
p.dstPos = zp;
p.dstPtr = D_data;
p.extent = extentV;
p.kind = cudaMemcpyHostToDevice;
cudaError err;
err = cudaMemcpy3D(&p);
ASTRA_CUDA_ASSERT(err);
return err == cudaSuccess;
}
bool copyProjectionsToDevice(const float* data, cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch)
{
if (!pitch)
pitch = dims.iProjU;
cudaPitchedPtr ptr;
ptr.ptr = (void*)data; // const cast away
ptr.pitch = pitch*sizeof(float);
ptr.xsize = dims.iProjU*sizeof(float);
ptr.ysize = dims.iProjAngles;
cudaExtent extentV;
extentV.width = dims.iProjU*sizeof(float);
extentV.height = dims.iProjAngles;
extentV.depth = dims.iProjV;
cudaPos zp = { 0, 0, 0 };
cudaMemcpy3DParms p;
p.srcArray = 0;
p.srcPos = zp;
p.srcPtr = ptr;
p.dstArray = 0;
p.dstPos = zp;
p.dstPtr = D_data;
p.extent = extentV;
p.kind = cudaMemcpyHostToDevice;
cudaError err;
err = cudaMemcpy3D(&p);
ASTRA_CUDA_ASSERT(err);
return err == cudaSuccess;
}
bool copyVolumeFromDevice(float* data, const cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch)
{
if (!pitch)
pitch = dims.iVolX;
cudaPitchedPtr ptr;
ptr.ptr = data;
ptr.pitch = pitch*sizeof(float);
ptr.xsize = dims.iVolX*sizeof(float);
ptr.ysize = dims.iVolY;
cudaExtent extentV;
extentV.width = dims.iVolX*sizeof(float);
extentV.height = dims.iVolY;
extentV.depth = dims.iVolZ;
cudaPos zp = { 0, 0, 0 };
cudaMemcpy3DParms p;
p.srcArray = 0;
p.srcPos = zp;
p.srcPtr = D_data;
p.dstArray = 0;
p.dstPos = zp;
p.dstPtr = ptr;
p.extent = extentV;
p.kind = cudaMemcpyDeviceToHost;
cudaError err;
err = cudaMemcpy3D(&p);
ASTRA_CUDA_ASSERT(err);
return err == cudaSuccess;
}
bool copyProjectionsFromDevice(float* data, const cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch)
{
if (!pitch)
pitch = dims.iProjU;
cudaPitchedPtr ptr;
ptr.ptr = data;
ptr.pitch = pitch*sizeof(float);
ptr.xsize = dims.iProjU*sizeof(float);
ptr.ysize = dims.iProjAngles;
cudaExtent extentV;
extentV.width = dims.iProjU*sizeof(float);
extentV.height = dims.iProjAngles;
extentV.depth = dims.iProjV;
cudaPos zp = { 0, 0, 0 };
cudaMemcpy3DParms p;
p.srcArray = 0;
p.srcPos = zp;
p.srcPtr = D_data;
p.dstArray = 0;
p.dstPos = zp;
p.dstPtr = ptr;
p.extent = extentV;
p.kind = cudaMemcpyDeviceToHost;
cudaError err;
err = cudaMemcpy3D(&p);
ASTRA_CUDA_ASSERT(err);
return err == cudaSuccess;
}
bool duplicateVolumeData(cudaPitchedPtr& D_dst, const cudaPitchedPtr& D_src, const SDimensions3D& dims)
{
cudaExtent extentV;
extentV.width = dims.iVolX*sizeof(float);
extentV.height = dims.iVolY;
extentV.depth = dims.iVolZ;
cudaPos zp = { 0, 0, 0 };
cudaMemcpy3DParms p;
p.srcArray = 0;
p.srcPos = zp;
p.srcPtr = D_src;
p.dstArray = 0;
p.dstPos = zp;
p.dstPtr = D_dst;
p.extent = extentV;
p.kind = cudaMemcpyDeviceToDevice;
cudaError err;
err = cudaMemcpy3D(&p);
ASTRA_CUDA_ASSERT(err);
return err == cudaSuccess;
}
bool duplicateProjectionData(cudaPitchedPtr& D_dst, const cudaPitchedPtr& D_src, const SDimensions3D& dims)
{
cudaExtent extentV;
extentV.width = dims.iProjU*sizeof(float);
extentV.height = dims.iProjAngles;
extentV.depth = dims.iProjV;
cudaPos zp = { 0, 0, 0 };
cudaMemcpy3DParms p;
p.srcArray = 0;
p.srcPos = zp;
p.srcPtr = D_src;
p.dstArray = 0;
p.dstPos = zp;
p.dstPtr = D_dst;
p.extent = extentV;
p.kind = cudaMemcpyDeviceToDevice;
cudaError err;
err = cudaMemcpy3D(&p);
ASTRA_CUDA_ASSERT(err);
return err == cudaSuccess;
}
// TODO: Consider using a single array of size max(proj,volume) (per dim)
// instead of allocating a new one each time
cudaArray* allocateVolumeArray(const SDimensions3D& dims)
{
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
cudaArray* cuArray;
cudaExtent extentA;
extentA.width = dims.iVolX;
extentA.height = dims.iVolY;
extentA.depth = dims.iVolZ;
cudaError err = cudaMalloc3DArray(&cuArray, &channelDesc, extentA);
if (err != cudaSuccess) {
mexPrintf("Failed to allocate %dx%dx%d GPU array", dims.iVolX, dims.iVolY, dims.iVolZ);
return 0;
}
return cuArray;
}
cudaArray* allocateProjectionArray(const SDimensions3D& dims)
{
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
cudaArray* cuArray;
cudaExtent extentA;
extentA.width = dims.iProjU;
extentA.height = dims.iProjAngles;
extentA.depth = dims.iProjV;
cudaError err = cudaMalloc3DArray(&cuArray, &channelDesc, extentA);
if (err != cudaSuccess) {
mexPrintf("Failed to allocate %dx%dx%d GPU array", dims.iProjU, dims.iProjAngles, dims.iProjV);
return 0;
}
return cuArray;
}
bool bindDataTexture(const cudaArray* array, texture3D & Texture, cudaTextureAddressMode bordermode, bool normalized)
{
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
Texture.addressMode[0] = bordermode;
Texture.addressMode[1] = bordermode;
Texture.addressMode[2] = bordermode;
Texture.filterMode = cudaFilterModeLinear;
Texture.normalized = normalized;
cudaError err = cudaBindTextureToArray(Texture, array, channelDesc);
checkLastError("cudaBindTextureToArray cudaMemcpy3D");
ASTRA_CUDA_ASSERT(err);
//mexPrintf("Max texture size !!! %i %i %i", cudaDeviceProp.maxTexture3D[0], cudaDeviceProp.maxTexture3D[1], cudaDeviceProp.maxTexture3D[2]);
return true;
}
cudaArray * transferDeformationToArray(const mxGPUArray * m_img)
{
mwSize const * dimensions = mxGPUGetDimensions(m_img);
mwSize Ndim = mxGPUGetNumberOfDimensions(m_img);
int M = (int)dimensions[0];
int N = (int)dimensions[1];
int O = Ndim > 2 ? (int)dimensions[2] : 1;
SDimensions3D dims;
dims.iVolX = M;
dims.iVolY = N;
dims.iVolZ = O;
//mexPrintf("Deformation field size: %i %i %i \n", M,N,O);
cudaArray* array = allocateVolumeArray(dims);
// get the values into float array
const float * img =(const float *)mxGPUGetDataReadOnly(m_img);
if (array == 0)
return 0;
if (M * sizeof(float) > 2048) {
mexPrintf("Volume is too large to be transfered to GPU array");
return 0;
}
// make volume array (no copying)
cudaPitchedPtr volume;
volume.ptr = (float *)img;
volume.pitch = M * sizeof(float);
volume.xsize = M;
volume.ysize = N;
transferVolumeToArray(volume, array,dims);
// if (!checkLastError("transferDeformToArray cudaMemcpy3D"))
// return false;
return array;
}
bool transferVolumeToArray(cudaPitchedPtr D_volumeData, cudaArray* array, const SDimensions3D& dims)
{
cudaExtent extentA;
extentA.width = dims.iVolX;
extentA.height = dims.iVolY;
extentA.depth = dims.iVolZ;
cudaMemcpy3DParms p;
cudaPos zp = { 0, 0, 0 };
p.srcArray = 0;
p.srcPos = zp;
p.srcPtr = D_volumeData;
p.dstArray = array;
p.dstPtr.ptr = 0;
p.dstPtr.pitch = 0;
p.dstPtr.xsize = 0;
p.dstPtr.ysize = 0;
p.dstPos = zp;
p.extent = extentA;
p.kind = cudaMemcpyDeviceToDevice;
cudaError err = cudaMemcpy3D(&p);
checkLastError("transferVolumeToArray cudaMemcpy3D");
ASTRA_CUDA_ASSERT(err);
// TODO: check errors
return true;
}
bool transferProjectionsToArray(cudaPitchedPtr D_projData, cudaArray* array, const SDimensions3D& dims)
{
cudaExtent extentA;
extentA.width = dims.iProjU;
extentA.height = dims.iProjAngles;
extentA.depth = dims.iProjV;
cudaMemcpy3DParms p;
cudaPos zp = { 0, 0, 0 };
p.srcArray = 0;
p.srcPos = zp;
p.srcPtr = D_projData;
p.dstArray = array;
p.dstPtr.ptr = 0;
p.dstPtr.pitch = 0;
p.dstPtr.xsize = 0;
p.dstPtr.ysize = 0;
p.dstPos = zp;
p.extent = extentA;
p.kind = cudaMemcpyDeviceToDevice;
cudaError err = cudaMemcpy3D(&p);
checkLastError("transferProjectionsToArray cudaMemcpy3D");
ASTRA_CUDA_ASSERT(err);
// TODO: check errors
return true;
}
bool cudaTextForceKernelsCompletion()
{
cudaError_t returnedCudaError = cudaThreadSynchronize();
if (returnedCudaError != cudaSuccess) {
//FIXME
fprintf(stderr, "Failed to force completion of cuda kernels: %d: %s. \n ", returnedCudaError, cudaGetErrorString(returnedCudaError));
ASTRA_ERROR("Failed to force completion of cuda kernels: %d: %s.\n ", returnedCudaError, cudaGetErrorString(returnedCudaError));
return false;
}
return true;
}
void reportCudaError(cudaError_t err)
{
if (err != cudaSuccess) {
mexPrintf("CUDA error %d: %s.", err, cudaGetErrorString(err));
mexErrMsgTxt("ASTRA failed, reboot GPU");
}
}
//
//float dotproduct3d(cudapitchedptr data, unsigned int x, unsigned int y,
// unsigned int z)
//{
// return astraCUDA3d::dotproduct2d((float*)data.ptr, data.pitch/sizeof(float), x, y*z);
//}
int calcNextPowerOfTwo(int _iValue)
{
int iOutput = 1;
while (iOutput < _iValue)
iOutput *= 2;
return iOutput;
}
double tic()
{
return clock();
}
double toc(double tstart)
{
return (clock() - tstart) / CLOCKS_PER_SEC;
}
void printFreeMemory()
{
// show memory usage of GPU
size_t free_byte;
size_t total_byte;
cudaError_t cuda_status = cudaMemGetInfo(&free_byte, &total_byte);
if (cudaSuccess != cuda_status){
mexPrintf("Error: cudaMemGetInfo fails, %s \n", cudaGetErrorString(cuda_status));
}
double free_db = (double)free_byte;
double total_db = (double)total_byte;
double used_db = total_db - free_db;
mexPrintf("GPU memory usage: used = %g, free = %g MB, total = %g MB\n",
used_db / 1024.0 / 1024.0, free_db / 1024.0 / 1024.0, total_db / 1024.0 / 1024.0);
}
int checkLastError(char * msg)
{
cudaError_t cudaStatus = cudaGetLastError();
if (cudaStatus != cudaSuccess) {
char err[512];
sprintf(err, "astraCUDA3d failed %s: %s. \n", msg, cudaGetErrorString(cudaStatus));
mexErrMsgTxt(err);
//mexPrintf(err);
//mexPrintf("assert \n");
//ASTRA_CUDA_ASSERT(cudaStatus);
}
return 0;
}
int dumpArray(char* filename, int width, int height, float *buffer)
{
FILE * f;
int i, j;
f = fopen(filename, "w");
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
fprintf(f, "%3.2g\t", buffer[i*width + j]);
// fprintf(f, "%i %i\t", i, j);
//fprintf(f, "%3.2g\t", 1);
}
fprintf(f, "\n");
}
fclose(f);
return 0;
}
int dumpCudaArray(cudaPitchedPtr Data, int start, int end, char * filename)
{
char fname[32], msg[32];
int width = Data.xsize / sizeof(float);
int height = Data.ysize;
int slice_size = width*height*sizeof(float);
float* buffer = new float[width*height];
for (int i = start; i < end; i++) {
cudaMemcpy(buffer, ((float*)Data.ptr) + slice_size*i, slice_size, cudaMemcpyDeviceToHost);
sprintf(fname, filename, i);
sprintf(msg, filename, i);
fprintf(stdout, "%s\n", msg);
dumpArray(fname, width, height, buffer);
}
return 0;
}
int writeImageCudaArray(cudaPitchedPtr Data, int start, int end, char * filename)
{
char fname[32];
int width = Data.xsize / sizeof(float);
int height = Data.ysize;
int slice_size = width*height*sizeof(float);
float* buffer = new float[width*height];
for (int i = start; i < end; i++) {
cudaMemcpy(buffer, ((float*)Data.ptr) + slice_size*i, slice_size, cudaMemcpyDeviceToHost);
sprintf(fname, filename, i);
writeImage(fname, width, height, buffer);
}
return 0;
}
int writeImage(char * fname, int w, int h, float * data)
{
// normalize image
float max = 0;
for (int i = 0; i < w*h; i++)
if (data[i] > max)
max = data[i];
float **x;
/* allocate the array */
x = (float **)malloc(h * sizeof *x);
for (int i = 0; i<h; i++)
x[i] = (float *)malloc(w * sizeof *x[i]);
for (int i = 0; i<h; i++)
for (int j = 0; j < w; j++)
x[i][j] = data[i*w + j] / max; // fill the array
writeBMPImage(fname, w,h, x,x,x);
return 0;
}
int writeBMPImage(char * fname, int w, int h, float ** red, float ** green, float ** blue)
{
FILE *f;
unsigned char *img = NULL;
int filesize = 54 + 3 * w*h; //w is your image width, h is image height, both int
if (img)
free(img);
img = (unsigned char *)malloc(3 * w*h);
memset(img, 0, sizeof(img));
float r, g, b;
int x, y;
for (int i = 0; i<w; i++)
{
for (int j = 0; j<h; j++)
{
x = i; y = (h - 1) - j;
r = red[i][j] * 255;
g = green[i][j] * 255;
b = blue[i][j] * 255;
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
img[(x + y*w) * 3 + 2] = (unsigned char)(r);
img[(x + y*w) * 3 + 1] = (unsigned char)(g);
img[(x + y*w) * 3 + 0] = (unsigned char)(b);
}
}
unsigned char bmpfileheader[14] = { 'B', 'M', 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0 };
unsigned char bmpinfoheader[40] = { 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 24, 0 };
unsigned char bmppad[3] = { 0, 0, 0 };
bmpfileheader[2] = (unsigned char)(filesize);
bmpfileheader[3] = (unsigned char)(filesize >> 8);
bmpfileheader[4] = (unsigned char)(filesize >> 16);
bmpfileheader[5] = (unsigned char)(filesize >> 24);
bmpinfoheader[4] = (unsigned char)(w);
bmpinfoheader[5] = (unsigned char)(w >> 8);
bmpinfoheader[6] = (unsigned char)(w >> 16);
bmpinfoheader[7] = (unsigned char)(w >> 24);
bmpinfoheader[8] = (unsigned char)(h);
bmpinfoheader[9] = (unsigned char)(h >> 8);
bmpinfoheader[10] = (unsigned char)(h >> 16);
bmpinfoheader[11] = (unsigned char)(h >> 24);
f = fopen(fname, "wb");
fwrite(bmpfileheader, 1, 14, f);
fwrite(bmpinfoheader, 1, 40, f);
for (int i = 0; i < h; i++)
{
fwrite(img + (w*(h - i - 1) * 3), 3, w, f);
fwrite(bmppad, 1, (4 - (w * 3) % 4) % 4, f);
}
fclose(f);
fprintf(stdout, "Saved image %s\n", fname);
return 0;
}
}