initial commit

This commit is contained in:
2026-08-07 15:56:42 +09:00
commit 91ad25aca9
1012 changed files with 159314 additions and 0 deletions
@@ -0,0 +1,353 @@
/*
*-----------------------------------------------------------------------*
|                                                                       |
|  Except where otherwise noted, this work is licensed under a          |
|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            |
|  International (CC BY-NC-SA 4.0) license.                             |
|                                                                       |
|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    |
|                                                                       |
|      Author: CXS group, PSI  |
*-----------------------------------------------------------------------*
You may use this code with the following provisions:
If the code is fully or partially redistributed, or rewritten in another
computing language this notice should be included in the redistribution.
If this code, or subfunctions or parts of it, is used for research in a
publication or if it is fully or partially rewritten for another
computing language the authors and institution should be acknowledged
in written form in the publication: “Data processing was carried out
using the “cSAXS matlab package” developed by the CXS group,
Paul Scherrer Institut, Switzerland.”
Variations on the latter text can be incorporated upon discussion with
the CXS group if needed to more specifically reflect the use of the package
for the published work.
A publication that focuses on describing features, or parameters, that
are already existing in the code should be first discussed with the
authors.
This code and subroutines are part of a continuous development, they
are provided “as they are” without guarantees or liability on part
of PSI or the authors. It is the user responsibility to ensure its
proper use and the correctness of the results.
*/
// Defines the exported functions for the DLL application.
//
// recompile commands
// (Linux, GCC 4.8.5) mexcuda -outdir private ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper/util3d.cu ASTRA_GPU_wrapper/par3d_fp.cu ASTRA_GPU_wrapper/par3d_bp.cu
// (Windows) mexcuda -outdir private ASTRA_GPU_wrapper\ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper\util3d.cu ASTRA_GPU_wrapper\par3d_fp.cu ASTRA_GPU_wrapper\par3d_bp.cu
/************* INPUTS *****************************/
/*
string 'fp' or 'bp' - forward / backward projection
single gpuArray volume or data object
struct cfg - contain configuration for astra, created by ASTRA_initialize.m
double array vec - contain projection geometry for astra, created by ASTRA_initialize.m
(optional)
single gpuArray - volume or data object to write the results to
*/
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <cuda.h>
#include <stdio.h>
#include <cstdio>
#include <cassert>
#include <iostream>
#include <list>
#include "mex.h"
#include "gpu/mxGPUArray.h"
#include "util3d.h"
#include "dims3d.h"
#include "par3d_bp.h"
#include "par3d_fp.h"
void mexFunction(int nlhs, mxArray *plhs[],
int nrhs, mxArray const *prhs[])
{
//mexPrintf("Warning: loading development version of ASTRA\n");
//mexPrintf("Ninputs:%i\n", nrhs);
if (!((nrhs == 4) || (nrhs == 5) || (nrhs == 8 ) || (nrhs == 11 ) ))
mexErrMsgTxt("4,5, 8, or 11 input arguments required");
using namespace astraCUDA3d;
char const * const errId = "parallel:gpu:mexGPUExample:InvalidInput";
char const * const errMsg = "Invalid input to MEX file.";
/* Throw an error if the input is not a GPU array. */
if (!mxIsGPUArray(prhs[1])) {
mexErrMsgIdAndTxt(errId, "The second input must be GPU array");
}
/* Load configuration */
SDimensions3D dims;
mxArray * tmp;
double * val;
#define SETVAR(name) do {tmp = mxGetField(prhs[2], 0, ""#name""); if (tmp!=NULL) { val = mxGetPr(tmp); dims.name = (unsigned int)val[0]; }} while (0);
SETVAR(iVolX);
SETVAR(iVolY);
SETVAR(iVolZ);
SETVAR(iProjAngles);
SETVAR(iProjU);
SETVAR(iProjV);
SETVAR(iRaysPerDetDim);
SETVAR(iRaysPerVoxelDim);
#undef SETVAR
/* Initialize the MathWorks GPU API. */
mxInitGPU();
/* load confuguration of angles */
double * my_angles = mxGetPr(prhs[3]);
int Nangles = (int)mxGetM(prhs[3]);
SPar3DProjection* angle = new SPar3DProjection[Nangles];
#define SETVAR(name,i,j) do { angle[i].name = my_angles[i+j*Nangles]; } while (0);
for (int i = 0; i < Nangles; i++)
{
SETVAR(fRayX, i, 0);
SETVAR(fRayY, i, 1);
SETVAR(fRayZ, i, 2);
SETVAR(fDetSX, i, 3);
SETVAR(fDetSY, i, 4);
SETVAR(fDetSZ, i, 5);
SETVAR(fDetUX, i, 6);
SETVAR(fDetUY, i, 7);
SETVAR(fDetUZ, i, 8);
SETVAR(fDetVX, i, 9);
SETVAR(fDetVY, i, 10);
SETVAR(fDetVZ, i, 11);
// mexPrintf("---------------------- \n");
}
#undef SETVAR
char * task = mxArrayToString(prhs[0]);
//mexPrintf("--------- Task %s \n ", task);
/* Load input data */
mxGPUArray const * m_data = mxGPUCreateFromMxArray(prhs[1]);
if ((mxGPUGetClassID(m_data) != mxSINGLE_CLASS)) {
mexErrMsgIdAndTxt(errId, errMsg);
}
float * p_data = (float *)mxGPUGetDataReadOnly(m_data);
DeformField DF;
if (nrhs == 8 || nrhs == 11 ) {
/* load deformation field */
DF.use_deform = true;
DF.use_linear_model = false; // assume contant deformation
DF.X0 = mxGPUCreateFromMxArray(prhs[5]);
DF.Y0 = mxGPUCreateFromMxArray(prhs[6]);
DF.Z0 = mxGPUCreateFromMxArray(prhs[7]);
if ((mxGPUGetClassID(DF.X0) != mxSINGLE_CLASS) |
(mxGPUGetClassID(DF.Y0) != mxSINGLE_CLASS) |
(mxGPUGetClassID(DF.Z0) != mxSINGLE_CLASS)) {
mexPrintf("wrong input type: deformation fields has to be single\n");
mexErrMsgIdAndTxt(errId, errMsg);
}
if (nrhs == 11 ) {
DF.use_linear_model = true; // assume linear deformation
DF.X1 = mxGPUCreateFromMxArray(prhs[8]);
DF.Y1 = mxGPUCreateFromMxArray(prhs[9]);
DF.Z1 = mxGPUCreateFromMxArray(prhs[10]);
if ((mxGPUGetClassID(DF.X1) != mxSINGLE_CLASS) |
(mxGPUGetClassID(DF.Y1) != mxSINGLE_CLASS) |
(mxGPUGetClassID(DF.Z1) != mxSINGLE_CLASS)) {
mexPrintf("wrong input type: deformation fields has to be single\n");
mexErrMsgIdAndTxt(errId, errMsg);
}
}
}
else
DF.use_deform = false;
if (strcmp(task, "fp")==0)
{
//mexPrintf(" forward projection \n ");
/* make volume array (no copying) */
cudaPitchedPtr volData;
volData.ptr = p_data;
volData.pitch = dims.iVolX * sizeof(float);
volData.xsize = dims.iVolX;
volData.ysize = dims.iVolY;
mxGPUArray * m_projData;
if(nrhs >= 5 && !mxIsEmpty(prhs[4]) )
{
/**** copy of the array is the slow operation and also GPU memory is limited *****/
// m_projData = mxGPUCopyFromMxArray(prhs[4]);
/* Use ugly trick to write directly to the provided GPU array ...
=> Now it is writting directly into the input field !!! DANGEROUS */
m_projData = const_cast<mxGPUArray*>(mxGPUCreateFromMxArray(prhs[4]));
if ((mxGPUGetClassID(m_projData) != mxSINGLE_CLASS)) {
mexPrintf("m_projData\n");
mexErrMsgIdAndTxt(errId, errMsg);
}
const mwSize * projSize = mxGPUGetDimensions(m_projData);
if (dims.iProjU != projSize[0] ||
dims.iProjV != projSize[1] ||
dims.iProjAngles != projSize[2])
mexErrMsgIdAndTxt(errId, "Wrong size of the inputs array");
//mexPrintf("Writting directly to the input array\n\n");
}
else
{
/* allocate projection field */
int const Ndim = 3;
mwSize projSize[3];
projSize[0] = (mwSize)dims.iProjU;
projSize[1] = (mwSize)dims.iProjV;
projSize[2] = (mwSize)dims.iProjAngles;
m_projData = mxGPUCreateGPUArray(Ndim,
projSize,
mxSINGLE_CLASS,
mxREAL,
MX_GPU_INITIALIZE_VALUES);
}
/* make cudaPitchedPtr for projection field */
cudaPitchedPtr projData;
projData.ptr = (float *)mxGPUGetData(m_projData);
projData.pitch = dims.iProjU * sizeof(float);
projData.xsize = dims.iProjU;
projData.ysize = dims.iProjV;
//mexPrintf("astraCUDA3d::Par3DFP \n ") ;
astraCUDA3d::Par3DFP(volData, projData, dims, angle, 1.0f, DF);
checkLastError("After Projector");
/* Wrap the result up as a MATLAB gpuArray for return. */
if (nlhs > 0)
plhs[0] = mxGPUCreateMxArrayOnGPU(m_projData);
mxGPUDestroyGPUArray(m_projData);
mxGPUDestroyGPUArray(m_data);
}
else if (strcmp(task, "bp")==0)
{
//mexPrintf(" backward projection \n ");
/* make projection field (no copying) */
cudaPitchedPtr projData;
projData.ptr = p_data;
projData.pitch = dims.iProjU * sizeof(float);
projData.xsize = dims.iProjU;
projData.ysize = dims.iProjAngles;
mxGPUArray* m_volData;
if(nrhs >= 5 && !mxIsEmpty(prhs[4]) )
{
/**** copy of the array is the slow operation and also GPU memory is limited *****/
// m_volData = mxGPUCopyFromMxArray(prhs[4]);
/* Use ugly trick to write directly to the provided GPU array ...
=> Now it is writting directly into the input field !!! DANGEROUS */
m_volData = const_cast<mxGPUArray*>(mxGPUCreateFromMxArray(prhs[4]));
if ((mxGPUGetClassID(m_volData) != mxSINGLE_CLASS)) {
mexPrintf("m_volData\n");
mexErrMsgIdAndTxt(errId, errMsg);
}
mwSize volSize[3];
const mwSize * volSize0 = mxGPUGetDimensions(m_volData);
if (mxGPUGetNumberOfDimensions(m_volData)==3) {
volSize[0]=volSize0[0];
volSize[1]=volSize0[1];
volSize[2]=volSize0[2];
} else {
volSize[0]=volSize0[0];
volSize[1]=volSize0[1];
volSize[2]=1;
}
if (dims.iVolX != volSize[0] ||
dims.iVolY != volSize[1] ||
dims.iVolZ != volSize[2])
mexErrMsgIdAndTxt(errId, "Wrong size of the inputs array");
} else {
/* allocate volume data */
int const Ndim = 3;
mwSize volSize[3];
volSize[0] = (mwSize)dims.iVolX;
volSize[1] = (mwSize)dims.iVolY;
volSize[2] = (mwSize)dims.iVolZ;
m_volData = mxGPUCreateGPUArray(Ndim,
volSize,
mxSINGLE_CLASS,
mxREAL,
MX_GPU_INITIALIZE_VALUES);
}
/* make volume array pointer*/
cudaPitchedPtr volData;
volData.ptr = (float *)mxGPUGetData(m_volData);
volData.pitch = dims.iVolX * sizeof(float);
volData.xsize = dims.iVolX;
volData.ysize = dims.iVolY;
astraCUDA3d::Par3DBP(volData, projData, dims, angle, 1.0f, DF);
checkLastError("After Projector");
/* Wrap the result up as a MATLAB gpuArray for return. */
if (nlhs > 0)
plhs[0] = mxGPUCreateMxArrayOnGPU(m_volData);
mxGPUDestroyGPUArray(m_volData);
mxGPUDestroyGPUArray(m_data);
}
else
mexPrintf("No such option");
if (DF.use_deform) {
//mexPrintf("Deleted DF");
mxGPUDestroyGPUArray(DF.X0);
mxGPUDestroyGPUArray(DF.Y0);
mxGPUDestroyGPUArray(DF.Z0);
if (DF.use_linear_model) {
mxGPUDestroyGPUArray(DF.X1);
mxGPUDestroyGPUArray(DF.Y1);
mxGPUDestroyGPUArray(DF.Z1);
}
}
}
@@ -0,0 +1,143 @@
/*
-----------------------------------------------------------------------
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$
*/
#ifndef _INC_ASTRA_GEOMETRYUTIL3D
#define _INC_ASTRA_GEOMETRYUTIL3D
namespace astra {
struct SConeProjection {
// the source
double fSrcX, fSrcY, fSrcZ;
// the origin ("bottom left") of the (flat-panel) detector
double fDetSX, fDetSY, fDetSZ;
// the U-edge of a detector pixel
double fDetUX, fDetUY, fDetUZ;
// the V-edge of a detector pixel
double fDetVX, fDetVY, fDetVZ;
void translate(double dx, double dy, double dz) {
fSrcX += dx;
fSrcY += dy;
fSrcZ += dz;
fDetSX += dx;
fDetSY += dy;
fDetSZ += dz;
}
void scale(double factor) {
fSrcX *= factor;
fSrcY *= factor;
fSrcZ *= factor;
fDetSX *= factor;
fDetSY *= factor;
fDetSZ *= factor;
fDetUX *= factor;
fDetUY *= factor;
fDetUZ *= factor;
fDetVX *= factor;
fDetVY *= factor;
fDetVZ *= factor;
}
};
struct SPar3DProjection {
// the ray direction
double fRayX, fRayY, fRayZ;
// the origin ("bottom left") of the (flat-panel) detector
double fDetSX, fDetSY, fDetSZ;
// the U-edge of a detector pixel
double fDetUX, fDetUY, fDetUZ;
// the V-edge of a detector pixel
double fDetVX, fDetVY, fDetVZ;
void translate(double dx, double dy, double dz) {
fDetSX += dx;
fDetSY += dy;
fDetSZ += dz;
}
void scale(double factor) {
fRayX *= factor;
fRayY *= factor;
fRayZ *= factor;
fDetSX *= factor;
fDetSY *= factor;
fDetSZ *= factor;
fDetUX *= factor;
fDetUY *= factor;
fDetUZ *= factor;
fDetVX *= factor;
fDetVY *= factor;
fDetVZ *= factor;
}
};
void computeBP_UV_Coeffs(const SPar3DProjection& proj,
double &fUX, double &fUY, double &fUZ, double &fUC,
double &fVX, double &fVY, double &fVZ, double &fVC);
void computeBP_UV_Coeffs(const SConeProjection& proj,
double &fUX, double &fUY, double &fUZ, double &fUC,
double &fVX, double &fVY, double &fVZ, double &fVC,
double &fDX, double &fDY, double &fDZ, double &fDC);
SConeProjection* genConeProjections(unsigned int iProjAngles,
unsigned int iProjU,
unsigned int iProjV,
double fOriginSourceDistance,
double fOriginDetectorDistance,
double fDetUSize,
double fDetVSize,
const float *pfAngles);
SPar3DProjection* genPar3DProjections(unsigned int iProjAngles,
unsigned int iProjU,
unsigned int iProjV,
double fDetUSize,
double fDetVSize,
const float *pfAngles);
}
#endif
+318
View File
@@ -0,0 +1,318 @@
/*
-----------------------------------------------------------------------
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$
*/
#ifndef _INC_ASTRA_GLOBALS
#define _INC_ASTRA_GLOBALS
/*! \mainpage The ASTRA-toolbox
*
* <img src="../images/logo_big.png"/>
*/
//----------------------------------------------------------------------------------------
#ifdef _MSC_VER
// disable warning: 'fopen' was declared deprecated
#pragma warning (disable : 4996)
// disable warning: C++ exception handler used, but unwind semantics are not enables
#pragma warning (disable : 4530)
// disable warning: no suitable definition provided for explicit template instantiation request
#pragma warning (disable : 4661)
#endif
//----------------------------------------------------------------------------------------
// standard includes
#include <cassert>
#include <iostream>
#include <fstream>
#include <math.h>
//#include <boost/static_assert.hpp>
//#include <boost/throw_exception.hpp>
//----------------------------------------------------------------------------------------
// macro's
#define ASTRA_TOOLBOXVERSION_MAJOR 1
#define ASTRA_TOOLBOXVERSION_MINOR 7
#define ASTRA_TOOLBOXVERSION ((ASTRA_TOOLBOXVERSION_MAJOR)*100 + (ASTRA_TOOLBOXVERSION_MINOR))
#define ASTRA_TOOLBOXVERSION_STRING "1.7.1"
#define ASTRA_ASSERT(a) assert(a)
#define ASTRA_CONFIG_CHECK(value, type, msg) if (!(value)) { cout << "Configuration Error in " << type << ": " << msg << endl; return false; }
#define ASTRA_CONFIG_WARNING(type, msg) { cout << "Warning in " << type << ": " << msg << endl; }
#define ASTRA_DELETE(a) if (a) { delete a; a = NULL; }
#define ASTRA_DELETE_ARRAY(a) if (a) { delete[] a; a = NULL; }
#ifdef _MSC_VER
#ifdef DLL_EXPORTS
#define _AstraExport __declspec(dllexport)
#define EXPIMP_TEMPLATE
#else
#define _AstraExport __declspec(dllimport)
#define EXPIMP_TEMPLATE extern
#endif
#else
#define _AstraExport
#endif
//----------------------------------------------------------------------------------------
// typedefs
namespace astra {
typedef float float32;
typedef double float64;
typedef unsigned short int uint16;
typedef signed short int sint16;
typedef unsigned char uchar8;
typedef signed char schar8;
typedef int int32;
typedef short int int16;
}
//----------------------------------------------------------------------------------------
// globals vars & functions
//namespace astra {
//#define ToolboxVersion 0.1f;
//float32 getVersion() { return ToolboxVersion; }
//_AstraExport bool cudaEnabled() {
//#ifdef ASTRA_CUDA
// return true;
//#else
// return false;
//#endif
//}
//}
//----------------------------------------------------------------------------------------
// errors
namespace astra {
typedef enum {ASTRA_SUCCESS,
ASTRA_ERROR_NOT_INITIALIZED,
ASTRA_ERROR_INVALID_FILE,
ASTRA_ERROR_OUT_OF_RANGE,
ASTRA_ERROR_DIMENSION_MISMATCH,
ASTRA_ERROR_EXTERNAL_LIBRARY,
ASTRA_ERROR_ALLOCATION,
ASTRA_ERROR_NOT_IMPLEMENTED} AstraError;
}
//----------------------------------------------------------------------------------------
// variables
namespace astra {
const float32 PI = 3.14159265358979323846264338328f;
const float32 PI32 = 3.14159265358979323846264338328f;
const float32 PIdiv2 = PI / 2;
const float32 PIdiv4 = PI / 4;
const float32 eps = 1e-7f;
extern _AstraExport bool running_in_matlab;
}
//----------------------------------------------------------------------------------------
// math
namespace astra {
inline float32 cos_73s(float32 x)
{
/*
const float32 c1 = 0.999999953464f;
const float32 c2 = -0.4999999053455f;
const float32 c3 = 0.0416635846769f;
const float32 c4 = -0.0013853704264f;
const float32 c5 = 0.000023233f;
*/
const float c1= (float)0.99940307;
const float c2= (float)-0.49558072;
const float c3= (float)0.03679168;
float32 x2;
x2 = x * x;
//return (c1 + x2*(c2 + x2*(c3 + x2*(c4 + c5*x2))));
return (c1 + x2*(c2 + c3 * x2));
}
inline float32 fast_cos(float32 x)
{
int quad;
//x = fmod(x, 2*PI); // Get rid of values > 2* pi
if (x < 0) x = -x; // cos(-x) = cos(x)
quad = int(x/PIdiv2); // Get quadrant # (0 to 3)
switch (quad) {
case 0: return cos_73s(x);
case 1: return -cos_73s(PI-x);
case 2: return -cos_73s(x-PI);
case 3: return cos_73s(2*PI-x);
}
return 0.0f;
}
inline float32 fast_sin(float32 x){
return fast_cos(PIdiv2-x);
}
}
//----------------------------------------------------------------------------------------
// structs
namespace astra {
/**
* Struct for storing pixel weigths
**/
struct SPixelWeight
{
int m_iIndex;
float32 m_fWeight;
};
/**
* Struct combining some properties of a detector in 1D detector row
**/
struct SDetector2D
{
int m_iIndex;
int m_iAngleIndex;
int m_iDetectorIndex;
};
/**
* Struct combining some properties of a detector in 2D detector array
**/
struct SDetector3D
{
int m_iIndex;
int m_iAngleIndex;
int m_iDetectorIndex;
int m_iSliceIndex;
};
}
//----------------------------------------------------------------------------------------
// some toys
// safe reinterpret cast
// template <class To, class From>
// To safe_reinterpret_cast(From from)
// {
// BOOST_STATIC_ASSERT(sizeof(From) <= sizeof(To));
// return reinterpret_cast<To>(from);
// }
//----------------------------------------------------------------------------------------
// functions for testing
template<typename T>
inline void writeArray(T*** arr, int dim1, int dim2, int dim3, const std::string& filename)
{
std::ofstream out(filename.c_str());
int i1, i2, i3;
for (i1 = 0; i1 < dim1; ++i1) {
for (i2 = 0; i2 < dim2; ++i2) {
for (i3 = 0; i3 < dim3; ++i3) {
out << arr[i1][i2][i3] << " ";
}
out << std::endl;
}
out << std::endl;
}
out.close();
}
template<typename T>
inline void writeArray(T** arr, int dim1, int dim2, const std::string& filename)
{
std::ofstream out(filename.c_str());
for (int i1 = 0; i1 < dim1; i1++) {
for (int i2 = 0; i2 < dim2; i2++) {
out << arr[i1][i2] << " ";
}
out << std::endl;
}
out.close();
}
template<typename T>
inline void writeArray(T* arr, int dim1, const std::string& filename)
{
std::ofstream out(filename.c_str());
for (int i1 = 0; i1 < dim1; i1++) {
out << arr[i1] << " ";
}
out.close();
}
namespace astra {
_AstraExport inline int getVersion() { return ASTRA_TOOLBOXVERSION; }
_AstraExport inline const char* getVersionString() { return ASTRA_TOOLBOXVERSION_STRING; }
#ifdef ASTRA_CUDA
_AstraExport inline bool cudaEnabled() { return true; }
#else
_AstraExport inline bool cudaEnabled() { return false; }
#endif
}
//----------------------------------------------------------------------------------------
// portability between MSVC and Linux/gcc
#ifndef _MSC_VER
// #include "swrap.h"
#define EXPIMP_TEMPLATE
#if !defined(FORCEINLINE) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define FORCEINLINE inline __attribute__((__always_inline__))
#else
#define FORCEINLINE inline
#endif
#else
#define FORCEINLINE __forceinline
#endif
//----------------------------------------------------------------------------------------
// use pthreads on Linux and OSX
#if defined(__linux__) || defined(__MACH__)
#define USE_PTHREADS
#endif
#endif
+212
View File
@@ -0,0 +1,212 @@
/*
-----------------------------------------------------------------------
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$
*/
#define CLOG_MAIN
#include "clog.h"
#include "Logging.h"
#include <cstdio>
using namespace astra;
void CLogger::enableScreen()
{
m_bEnabledScreen = true;
}
void CLogger::enableFile()
{
m_bEnabledFile = true;
}
void CLogger::enable()
{
enableScreen();
enableFile();
}
void CLogger::disableScreen()
{
m_bEnabledScreen = false;
}
void CLogger::disableFile()
{
m_bEnabledFile = false;
}
void CLogger::disable()
{
disableScreen();
disableFile();
}
void CLogger::debug(const char *sfile, int sline, const char *fmt, ...)
{
_assureIsInitialized();
va_list ap, apf;
if(m_bEnabledScreen){
va_start(ap, fmt);
clog_debug(sfile,sline,0,fmt,ap);
va_end(ap);
}
if(m_bEnabledFile && m_bFileProvided){
va_start(apf, fmt);
clog_debug(sfile,sline,1,fmt,apf);
va_end(apf);
}
}
void CLogger::info(const char *sfile, int sline, const char *fmt, ...)
{
_assureIsInitialized();
va_list ap, apf;
if(m_bEnabledScreen){
va_start(ap, fmt);
clog_info(sfile,sline,0,fmt,ap);
va_end(ap);
}
if(m_bEnabledFile && m_bFileProvided){
va_start(apf, fmt);
clog_info(sfile,sline,1,fmt,apf);
va_end(apf);
}
}
void CLogger::warn(const char *sfile, int sline, const char *fmt, ...)
{
_assureIsInitialized();
va_list ap, apf;
if(m_bEnabledScreen){
va_start(ap, fmt);
clog_warn(sfile,sline,0,fmt,ap);
va_end(ap);
}
if(m_bEnabledFile && m_bFileProvided){
va_start(apf, fmt);
clog_warn(sfile,sline,1,fmt,apf);
va_end(apf);
}
}
void CLogger::error(const char *sfile, int sline, const char *fmt, ...)
{
_assureIsInitialized();
va_list ap, apf;
if(m_bEnabledScreen){
va_start(ap, fmt);
clog_error(sfile,sline,0,fmt,ap);
va_end(ap);
}
if(m_bEnabledFile && m_bFileProvided){
va_start(apf, fmt);
clog_error(sfile,sline,1,fmt,apf);
va_end(apf);
}
}
void CLogger::_setLevel(int id, log_level m_eLevel)
{
switch(m_eLevel){
case LOG_DEBUG:
clog_set_level(id,CLOG_DEBUG);
break;
case LOG_INFO:
clog_set_level(id,CLOG_INFO);
break;
case LOG_WARN:
clog_set_level(id,CLOG_WARN);
break;
case LOG_ERROR:
clog_set_level(id,CLOG_ERROR);
break;
}
}
void CLogger::setOutputScreen(int fd, log_level m_eLevel)
{
_assureIsInitialized();
if(fd==1||fd==2){
clog_set_fd(0, fd);
}else{
error(__FILE__,__LINE__,"Invalid file descriptor");
}
_setLevel(0,m_eLevel);
}
void CLogger::setOutputFile(const char *filename, log_level m_eLevel)
{
if(m_bFileProvided){
clog_free(1);
m_bFileProvided=false;
}
if(!clog_init_path(1,filename)){
m_bFileProvided=true;
_setLevel(1,m_eLevel);
}
}
void CLogger::_assureIsInitialized()
{
if(!m_bInitialized)
{
clog_init_fd(0, 2);
clog_set_level(0, CLOG_INFO);
clog_set_fmt(0, "%l: %m\n");
m_bInitialized = true;
}
}
void CLogger::setFormatFile(const char *fmt)
{
if(m_bFileProvided){
clog_set_fmt(1,fmt);
}else{
error(__FILE__,__LINE__,"No log file specified");
}
}
void CLogger::setFormatScreen(const char *fmt)
{
clog_set_fmt(0,fmt);
}
CLogger::CLogger()
{
;
}
bool CLogger::setCallbackScreen(void (*cb)(const char *msg, size_t len)){
_assureIsInitialized();
return clog_set_cb(0,cb)==0;
}
bool CLogger::m_bEnabledScreen = true;
bool CLogger::m_bEnabledFile = true;
bool CLogger::m_bFileProvided = false;
bool CLogger::m_bInitialized = false;
+164
View File
@@ -0,0 +1,164 @@
/*
-----------------------------------------------------------------------
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$
*/
#ifndef _INC_ASTRA_LOGGING
#define _INC_ASTRA_LOGGING
#include "Globals.h"
//#define ASTRA_DEBUG(...) astra::CLogger::debug(__FILE__,__LINE__, __VA_ARGS__)
//#define ASTRA_INFO(...) astra::CLogger::info(__FILE__,__LINE__, __VA_ARGS__)
//#define ASTRA_WARN(...) astra::CLogger::warn(__FILE__,__LINE__, __VA_ARGS__)
//#define ASTRA_ERROR(...) astra::CLogger::error(__FILE__,__LINE__, __VA_ARGS__)
// FIXME !!!!!!
#define ASTRA_DEBUG(...)
#define ASTRA_INFO(...)
#define ASTRA_WARN(...)
#define ASTRA_ERROR(...)
namespace astra
{
enum log_level {
LOG_DEBUG,
LOG_INFO,
LOG_WARN,
LOG_ERROR
};
class _AstraExport CLogger
{
CLogger();
~CLogger();
static bool m_bEnabledFile;
static bool m_bEnabledScreen;
static bool m_bFileProvided;
static bool m_bInitialized;
static void _assureIsInitialized();
static void _setLevel(int id, log_level m_eLevel);
public:
/**
* Writes a line to the log file (newline is added). Ignored if logging is turned off.
*
* @param sfile
* The name of the source file making this log call (e.g. __FILE__).
*
* @param sline
* The line number of the call in the source code (e.g. __LINE__).
*
* @param id
* The id of the logger to write to.
*
* @param fmt
* The format string for the message (printf formatting).
*
* @param ...
* Any additional format arguments.
*/
static void debug(const char *sfile, int sline, const char *fmt, ...);
static void info(const char *sfile, int sline, const char *fmt, ...);
static void warn(const char *sfile, int sline, const char *fmt, ...);
static void error(const char *sfile, int sline, const char *fmt, ...);
/**
* Sets the file to log to, with logging level.
*
* @param filename
* File to log to.
*
* @param m_eLevel
* Logging level (LOG_DEBUG, LOG_WARN, LOG_INFO, LOG_ERROR).
*
*/
static void setOutputFile(const char *filename, log_level m_eLevel);
/**
* Sets the screen to log to, with logging level.
*
* @param screen_fd
* Screen file descriptor (1 for stdout, 2 for stderr)
*
* @param m_eLevel
* Logging level (LOG_DEBUG, LOG_WARN, LOG_INFO, LOG_ERROR).
*
*/
static void setOutputScreen(int fd, log_level m_eLevel);
/**
* Set the format string for log messages. Here are the substitutions you may
* use:
*
* %f: Source file name generating the log call.
* %n: Source line number where the log call was made.
* %m: The message text sent to the logger (after printf formatting).
* %d: The current date, formatted using the logger's date format.
* %t: The current time, formatted using the logger's time format.
* %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR").
* %%: A literal percent sign.
*
* The default format string is "%d %t %f(%n): %l: %m\n".
*
* @param fmt
* The new format string, which must be less than 256 bytes.
* You probably will want to end this with a newline (\n).
*
*/
static void setFormatFile(const char *fmt);
static void setFormatScreen(const char *fmt);
/**
* Enable logging.
*
*/
static void enable();
static void enableScreen();
static void enableFile();
/**
* Disable logging.
*
*/
static void disable();
static void disableScreen();
static void disableFile();
/**
* Set callback function for logging to screen.
* @return whether callback was set succesfully.
*
*/
static bool setCallbackScreen(void (*cb)(const char *msg, size_t len));
};
}
#endif /* _INC_ASTRA_LOGGING */
+693
View File
@@ -0,0 +1,693 @@
/* clog: Extremely simple logger for C.
*
* Features:
* - Implemented purely as a single header file.
* - Create multiple loggers.
* - Four log levels (debug, info, warn, error).
* - Custom formats.
* - Fast.
*
* Dependencies:
* - Should conform to C89, C++98 (but requires vsnprintf, unfortunately).
* - POSIX environment.
*
* USAGE:
*
* Include this header in any file that wishes to write to logger(s). In
* exactly one file (per executable), define CLOG_MAIN first (e.g. in your
* main .c file).
*
* #define CLOG_MAIN
* #include "clog.h"
*
* This will define the actual objects that all the other units will use.
*
* Loggers are identified by integers (0 - 15). It's expected that you'll
* create meaningful constants and then refer to the loggers as such.
*
* Example:
*
* const int MY_LOGGER = 0;
*
* int main() {
* int r;
* r = clog_init_path(MY_LOGGER, "my_log.txt");
* if (r != 0) {
* fprintf(stderr, "Logger initialization failed.\n");
* return 1;
* }
* clog_info(CLOG(MY_LOGGER), "Hello, world!");
* clog_free(MY_LOGGER);
* return 0;
* }
*
* The CLOG macro used in the call to clog_info is a helper that passes the
* __FILE__ and __LINE__ parameters for you, so you don't have to type them
* every time. (It could be prettier with variadic macros, but that requires
* C99 or C++11 to be standards compliant.)
*
* Errors encountered by clog will be printed to stderr. You can suppress
* these by defining a macro called CLOG_SILENT before including clog.h.
*
* License: Do whatever you want. It would be nice if you contribute
* improvements as pull requests here:
*
* https://github.com/mmueller/clog
*
* Copyright 2013 Mike Mueller <mike@subfocal.net>.
*
* As is; no warranty is provided; use at your own risk.
*/
#ifndef __CLOG_H__
#define __CLOG_H__
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifndef _MSC_VER
#include <unistd.h>
#else
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
#define open _open
#define close _close
#define write _write
#define snprintf _snprintf
#endif
/* Number of loggers that can be defined. */
#define CLOG_MAX_LOGGERS 16
/* Format strings cannot be longer than this. */
#define CLOG_FORMAT_LENGTH 256
/* Formatted times and dates should be less than this length. If they are not,
* they will not appear in the log. */
#define CLOG_DATETIME_LENGTH 256
/* Default format strings. */
#define CLOG_DEFAULT_FORMAT "%d %t %f(%n): %l: %m\n"
#define CLOG_DEFAULT_DATE_FORMAT "%Y-%m-%d"
#define CLOG_DEFAULT_TIME_FORMAT "%H:%M:%S"
#ifdef __cplusplus
extern "C" {
#endif
enum clog_level {
CLOG_DEBUG,
CLOG_INFO,
CLOG_WARN,
CLOG_ERROR
};
struct clog;
/**
* Create a new logger writing to the given file path. The file will always
* be opened in append mode.
*
* @param id
* A constant integer between 0 and 15 that uniquely identifies this logger.
*
* @param path
* Path to the file where log messages will be written.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_init_path(int id, const char *const path);
/**
* Create a new logger writing to a file descriptor.
*
* @param id
* A constant integer between 0 and 15 that uniquely identifies this logger.
*
* @param fd
* The file descriptor where log messages will be written.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_init_fd(int id, int fd);
/**
* Destroy (clean up) a logger. You should do this at the end of execution,
* or when you are done using the logger.
*
* @param id
* The id of the logger to destroy.
*/
void clog_free(int id);
#define CLOG(id) __FILE__, __LINE__, id
/**
* Log functions (one per level). Call these to write messages to the log
* file. The first three arguments can be replaced with a call to the CLOG
* macro defined above, e.g.:
*
* clog_debug(CLOG(MY_LOGGER_ID), "This is a log message.");
*
* @param sfile
* The name of the source file making this log call (e.g. __FILE__).
*
* @param sline
* The line number of the call in the source code (e.g. __LINE__).
*
* @param id
* The id of the logger to write to.
*
* @param fmt
* The format string for the message (printf formatting).
*
* @param ...
* Any additional format arguments.
*/
void clog_debug(const char *sfile, int sline, int id, const char *fmt, va_list ap);
void clog_info(const char *sfile, int sline, int id, const char *fmt, va_list ap);
void clog_warn(const char *sfile, int sline, int id, const char *fmt, va_list ap);
void clog_error(const char *sfile, int sline, int id, const char *fmt, va_list ap);
/**
* Set the minimum level of messages that should be written to the log.
* Messages below this level will not be written. By default, loggers are
* created with level == CLOG_DEBUG.
*
* @param id
* The identifier of the logger.
*
* @param level
* The new minimum log level.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_set_level(int id, enum clog_level level);
/**
* Set the format string used for times. See strftime(3) for how this string
* should be defined. The default format string is CLOG_DEFAULT_TIME_FORMAT.
*
* @param fmt
* The new format string, which must be less than CLOG_FORMAT_LENGTH bytes.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_set_time_fmt(int id, const char *fmt);
/**
* Set the format string used for dates. See strftime(3) for how this string
* should be defined. The default format string is CLOG_DEFAULT_DATE_FORMAT.
*
* @param fmt
* The new format string, which must be less than CLOG_FORMAT_LENGTH bytes.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_set_date_fmt(int id, const char *fmt);
/**
* Set the format string for log messages. Here are the substitutions you may
* use:
*
* %f: Source file name generating the log call.
* %n: Source line number where the log call was made.
* %m: The message text sent to the logger (after printf formatting).
* %d: The current date, formatted using the logger's date format.
* %t: The current time, formatted using the logger's time format.
* %l: The log level (one of "DEBUG", "INFO", "WARN", or "ERROR").
* %%: A literal percent sign.
*
* The default format string is CLOG_DEFAULT_FORMAT.
*
* @param fmt
* The new format string, which must be less than CLOG_FORMAT_LENGTH bytes.
* You probably will want to end this with a newline (\n).
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_set_fmt(int id, const char *fmt);
/**
* Set the callback function.
*
* @param cb
* The new callback function.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_set_cb(int id, void (*cb)(const char *msg, size_t len));
/**
* Set the file descriptor.
*
* @param id
* The identifier of the logger.
*
* @param fd
* The new file descriptor.
*
* @return
* Zero on success, non-zero on failure.
*/
int clog_set_fd(int id, int fd);
/*
* No need to read below this point.
*/
/**
* The C logger structure.
*/
struct clog {
/* The current level of this logger. Messages below it will be dropped. */
enum clog_level level;
/* The file being written. */
int fd;
/* The format specifier. */
char fmt[CLOG_FORMAT_LENGTH];
/* Date format */
char date_fmt[CLOG_FORMAT_LENGTH];
/* Time format */
char time_fmt[CLOG_FORMAT_LENGTH];
/* Tracks whether the fd needs to be closed eventually. */
int opened;
/* Callback function for each log message. */
void (*cb)(const char *msg, size_t len);
};
void _clog_err(const char *fmt, ...);
#ifdef CLOG_MAIN
struct clog *_clog_loggers[CLOG_MAX_LOGGERS] = { 0 };
#else
extern struct clog *_clog_loggers[CLOG_MAX_LOGGERS];
#endif
#ifdef CLOG_MAIN
const char *const CLOG_LEVEL_NAMES[] = {
"Debug",
"Info",
"Warning",
"Error",
};
int
clog_init_path(int id, const char *const path)
{
int fd = open(path, O_CREAT | O_WRONLY | O_APPEND, 0666);
if (fd == -1) {
_clog_err("Unable to open %s: %s\n", path, strerror(errno));
return 1;
}
if (clog_init_fd(id, fd)) {
close(fd);
return 1;
}
_clog_loggers[id]->opened = 1;
return 0;
}
int
clog_init_fd(int id, int fd)
{
struct clog *logger;
if (_clog_loggers[id] != NULL) {
_clog_err("Logger %d already initialized.\n", id);
return 1;
}
logger = (struct clog *) malloc(sizeof(struct clog));
if (logger == NULL) {
_clog_err("Failed to allocate logger: %s\n", strerror(errno));
return 1;
}
logger->level = CLOG_DEBUG;
logger->fd = fd;
logger->opened = 0;
strcpy(logger->fmt, CLOG_DEFAULT_FORMAT);
strcpy(logger->date_fmt, CLOG_DEFAULT_DATE_FORMAT);
strcpy(logger->time_fmt, CLOG_DEFAULT_TIME_FORMAT);
logger->cb = NULL;
_clog_loggers[id] = logger;
return 0;
}
void
clog_free(int id)
{
if (_clog_loggers[id]) {
if (_clog_loggers[id]->opened) {
close(_clog_loggers[id]->fd);
}
free(_clog_loggers[id]);
_clog_loggers[id]=NULL;
}
}
int
clog_set_level(int id, enum clog_level level)
{
if (_clog_loggers[id] == NULL) {
return 1;
}
if ((unsigned) level > CLOG_ERROR) {
return 1;
}
_clog_loggers[id]->level = level;
return 0;
}
int
clog_set_fd(int id, int fd)
{
if (_clog_loggers[id] == NULL) {
return 1;
}
_clog_loggers[id]->fd = fd;
return 0;
}
int
clog_set_time_fmt(int id, const char *fmt)
{
struct clog *logger = _clog_loggers[id];
if (logger == NULL) {
_clog_err("clog_set_time_fmt: No such logger: %d\n", id);
return 1;
}
if (strlen(fmt) >= CLOG_FORMAT_LENGTH) {
_clog_err("clog_set_time_fmt: Format specifier too long.\n");
return 1;
}
strcpy(logger->time_fmt, fmt);
return 0;
}
int
clog_set_date_fmt(int id, const char *fmt)
{
struct clog *logger = _clog_loggers[id];
if (logger == NULL) {
_clog_err("clog_set_date_fmt: No such logger: %d\n", id);
return 1;
}
if (strlen(fmt) >= CLOG_FORMAT_LENGTH) {
_clog_err("clog_set_date_fmt: Format specifier too long.\n");
return 1;
}
strcpy(logger->date_fmt, fmt);
return 0;
}
int
clog_set_fmt(int id, const char *fmt)
{
struct clog *logger = _clog_loggers[id];
if (logger == NULL) {
_clog_err("clog_set_fmt: No such logger: %d\n", id);
return 1;
}
if (strlen(fmt) >= CLOG_FORMAT_LENGTH) {
_clog_err("clog_set_fmt: Format specifier too long.\n");
return 1;
}
strcpy(logger->fmt, fmt);
return 0;
}
int
clog_set_cb(int id, void (*cb)(const char *msg, size_t len))
{
struct clog *logger = _clog_loggers[id];
if (logger == NULL) {
_clog_err("clog_set_cb: No such logger: %d\n", id);
return 1;
}
logger->cb = cb;
return 0;
}
/* Internal functions */
size_t
_clog_append_str(char **dst, char *orig_buf, const char *src, size_t cur_size)
{
size_t new_size = cur_size;
while (strlen(*dst) + strlen(src) >= new_size) {
new_size *= 2;
}
if (new_size != cur_size) {
if (*dst == orig_buf) {
*dst = (char *) malloc(new_size);
strcpy(*dst, orig_buf);
} else {
*dst = (char *) realloc(*dst, new_size);
}
}
strcat(*dst, src);
return new_size;
}
size_t
_clog_append_int(char **dst, char *orig_buf, long int d, size_t cur_size)
{
char buf[40]; /* Enough for 128-bit decimal */
if (snprintf(buf, 40, "%ld", d) >= 40) {
return cur_size;
}
return _clog_append_str(dst, orig_buf, buf, cur_size);
}
size_t
_clog_append_time(char **dst, char *orig_buf, struct tm *lt,
const char *fmt, size_t cur_size)
{
char buf[CLOG_DATETIME_LENGTH];
size_t result = strftime(buf, CLOG_DATETIME_LENGTH, fmt, lt);
if (result > 0) {
return _clog_append_str(dst, orig_buf, buf, cur_size);
}
return cur_size;
}
const char *
_clog_basename(const char *path)
{
const char *slash = strrchr(path, '/');
if (slash) {
path = slash + 1;
}
#ifdef _WIN32
slash = strrchr(path, '\\');
if (slash) {
path = slash + 1;
}
#endif
return path;
}
char *
_clog_format(const struct clog *logger, char buf[], size_t buf_size,
const char *sfile, int sline, const char *level,
const char *message)
{
size_t cur_size = buf_size;
char *result = buf;
enum { NORMAL, SUBST } state = NORMAL;
size_t fmtlen = strlen(logger->fmt);
size_t i;
time_t t = time(NULL);
struct tm *lt = localtime(&t);
sfile = _clog_basename(sfile);
result[0] = 0;
for (i = 0; i < fmtlen; ++i) {
if (state == NORMAL) {
if (logger->fmt[i] == '%') {
state = SUBST;
} else {
char str[2] = { 0 };
str[0] = logger->fmt[i];
cur_size = _clog_append_str(&result, buf, str, cur_size);
}
} else {
switch (logger->fmt[i]) {
case '%':
cur_size = _clog_append_str(&result, buf, "%", cur_size);
break;
case 't':
cur_size = _clog_append_time(&result, buf, lt,
logger->time_fmt, cur_size);
break;
case 'd':
cur_size = _clog_append_time(&result, buf, lt,
logger->date_fmt, cur_size);
break;
case 'l':
cur_size = _clog_append_str(&result, buf, level, cur_size);
break;
case 'n':
cur_size = _clog_append_int(&result, buf, sline, cur_size);
break;
case 'f':
cur_size = _clog_append_str(&result, buf, sfile, cur_size);
break;
case 'm':
cur_size = _clog_append_str(&result, buf, message,
cur_size);
break;
}
state = NORMAL;
}
}
return result;
}
void
_clog_log(const char *sfile, int sline, enum clog_level level,
int id, const char *fmt, va_list ap)
{
/* For speed: Use a stack buffer until message exceeds 4096, then switch
* to dynamically allocated. This should greatly reduce the number of
* memory allocations (and subsequent fragmentation). */
char buf[4096];
size_t buf_size = 4096;
char *dynbuf = buf;
char *message;
int result;
struct clog *logger = _clog_loggers[id];
if (!logger) {
_clog_err("No such logger: %d\n", id);
return;
}
if (level < logger->level) {
return;
}
/* Format the message text with the argument list. */
result = vsnprintf(dynbuf, buf_size, fmt, ap);
if ((size_t) result >= buf_size) {
buf_size = result + 1;
dynbuf = (char *) malloc(buf_size);
result = vsnprintf(dynbuf, buf_size, fmt, ap);
if ((size_t) result >= buf_size) {
/* Formatting failed -- too large */
_clog_err("Formatting failed (1).\n");
free(dynbuf);
return;
}
}
/* Format according to log format and write to log */
{
char message_buf[4096];
message = _clog_format(logger, message_buf, 4096, sfile, sline,
CLOG_LEVEL_NAMES[level], dynbuf);
if (!message) {
_clog_err("Formatting failed (2).\n");
if (dynbuf != buf) {
free(dynbuf);
}
return;
}
result = write(logger->fd, message, strlen(message));
if (logger->cb) logger->cb(message,strlen(message));
if (result == -1) {
_clog_err("Unable to write to log file: %s\n", strerror(errno));
}
if (message != message_buf) {
free(message);
}
if (dynbuf != buf) {
free(dynbuf);
}
#ifndef _MSC_VER
fsync(logger->fd);
#else
HANDLE h = (HANDLE) _get_osfhandle(logger->fd);
if (h != INVALID_HANDLE_VALUE) {
// This call will fail on a console fd, but that's ok.
FlushFileBuffers(h);
}
#endif
}
}
void
clog_debug(const char *sfile, int sline, int id, const char *fmt, va_list ap)
{
_clog_log(sfile, sline, CLOG_DEBUG, id, fmt, ap);
}
void
clog_info(const char *sfile, int sline, int id, const char *fmt, va_list ap)
{
_clog_log(sfile, sline, CLOG_INFO, id, fmt, ap);
}
void
clog_warn(const char *sfile, int sline, int id, const char *fmt, va_list ap)
{
_clog_log(sfile, sline, CLOG_WARN, id, fmt, ap);
}
void
clog_error(const char *sfile, int sline, int id, const char *fmt, va_list ap)
{
_clog_log(sfile, sline, CLOG_ERROR, id, fmt, ap);
}
void
_clog_err(const char *fmt, ...)
{
#ifdef CLOG_SILENT
(void) fmt;
#else
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
#endif
}
#endif /* CLOG_MAIN */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* __CLOG_H__ */
+68
View File
@@ -0,0 +1,68 @@
/*
-----------------------------------------------------------------------
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$
*/
#ifndef _CUDA_CONE_DIMS_H
#define _CUDA_CONE_DIMS_H
#include "astra/GeometryUtil3D.h"
#include "mex.h"
#include "gpu/mxGPUArray.h"
namespace astraCUDA3d {
using astra::SConeProjection;
using astra::SPar3DProjection;
struct SDimensions3D {
unsigned int iVolX;
unsigned int iVolY;
unsigned int iVolZ;
unsigned int iProjAngles;
unsigned int iProjU; // number of detectors in the U direction
unsigned int iProjV; // number of detectors in the V direction
unsigned int iRaysPerDetDim;
unsigned int iRaysPerVoxelDim;
};
struct DeformField {
const mxGPUArray * X0;
const mxGPUArray * Y0;
const mxGPUArray * Z0;
const mxGPUArray * X1;
const mxGPUArray * Y1;
const mxGPUArray * Z1;
bool use_deform;
bool use_linear_model;
};
}
#endif
+19
View File
@@ -0,0 +1,19 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
+483
View File
@@ -0,0 +1,483 @@
/*
*-----------------------------------------------------------------------*
|                                                                       |
|  Except where otherwise noted, this work is licensed under a          |
|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            |
|  International (CC BY-NC-SA 4.0) license.                             |
|                                                                       |
|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    |
|                                                                       |
|      Author: CXS group, PSI  |
*-----------------------------------------------------------------------*
You may use this code with the following provisions:
If the code is fully or partially redistributed, or rewritten in another
computing language this notice should be included in the redistribution.
If this code, or subfunctions or parts of it, is used for research in a
publication or if it is fully or partially rewritten for another
computing language the authors and institution should be acknowledged
in written form in the publication: “Data processing was carried out
using the “cSAXS matlab package” developed by the CXS group,
Paul Scherrer Institut, Switzerland.”
Variations on the latter text can be incorporated upon discussion with
the CXS group if needed to more specifically reflect the use of the package
for the published work.
A publication that focuses on describing features, or parameters, that
are already existing in the code should be first discussed with the
authors.
This code and subroutines are part of a continuous development, they
are provided “as they are” without guarantees or liability on part
of PSI or the authors. It is the user responsibility to ensure its
proper use and the correctness of the results.
-----------------------------------------------------------------------
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 <iostream>
#include <list>
#include <cuda.h>
#include "util3d.h"
#ifdef STANDALONE
#include "par3d_fp.h"
#include "testutil.h"
#endif
#include "dims3d.h"
typedef texture<float, 3, cudaReadModeElementType> texture3D;
static texture3D gT_par3DProjTexture, Xdef0_tex, Ydef0_tex, Zdef0_tex, Xdef1_tex, Ydef1_tex, Zdef1_tex;
namespace astraCUDA3d {
#define ZSIZE 6
static const unsigned int g_volBlockZ = ZSIZE;
static const unsigned int g_anglesPerBlock = 32;
static const unsigned int g_volBlockX = 16;
static const unsigned int g_volBlockY = 32;
static const unsigned g_MaxAngles = 1024;
__constant__ float gC_C[8*g_MaxAngles];
#define MAX(x,y) (x>y?x:y);
#define MIN(x,y) (x<y?x:y);
#define ABS(x) (x>0?x:-x);
__global__ void dev_par3D_BP(void* D_volData, unsigned int volPitch,
int startAngle, int angleOffset, const SDimensions3D dims,
float fOutputScale, bool use_deform, bool linear_deform_model)
{
float* volData = (float*)D_volData;
int endAngle = startAngle + g_anglesPerBlock;
if (endAngle > dims.iProjAngles - angleOffset)
endAngle = dims.iProjAngles - angleOffset;
// threadIdx: x = rel x
// y = rel y
// blockIdx: x = x + y
// y = z
const int X = blockIdx.x % ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockX + threadIdx.x;
const int Y = blockIdx.x / ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockY + threadIdx.y;
if (X >= dims.iVolX)
return;
if (Y >= dims.iVolY)
return;
const int startZ = blockIdx.y * g_volBlockZ;
const float limX = dims.iVolX;
const float limY = dims.iVolY;
const float limZ = dims.iVolZ;
float fX = X - 0.5f*limX + 0.5f;
float fY = Y - 0.5f*limY + 0.5f;
float fZ = startZ - 0.5f*limZ + 0.5f;
// solve by small blocks over all angles
float Z[ZSIZE];
for(int i=0; i < ZSIZE; i++)
Z[i] = 0.0f;
float fAngle = startAngle + angleOffset + 0.5f;
float4 fCu, fCv;
float fU, fV;
float fXn, fYn, fZn; // normalized coordinates
float fXs, fYs, fZs; // shifted coordinates
float angle_ratio ; // ratio from angle / iProjAngles
for (int angle = startAngle; angle < endAngle; ++angle, fAngle += 1.0f)
{
fCu = make_float4(gC_C[8*angle+0], gC_C[8*angle+1], gC_C[8*angle+2], gC_C[8*angle+3]);
fCv = make_float4(gC_C[8*angle+4], gC_C[8*angle+5], gC_C[8*angle+6], gC_C[8*angle+7]);
angle_ratio = (float)angle / (float)dims.iProjAngles ;
if (use_deform)
{
/*
// FASTER APPROXIMATION FOR SMALL DEFORMATIONS
fXn = X/limX; // normalized coordinates
fYn = Y/limY;
fZn = startZ/limZ;
// load deformed coordinates
fXs = fX + tex3D(Xdef0_tex,fXn, fYn, fZn);
fYs = fY + tex3D(Ydef0_tex,fXn, fYn, fZn);
fZs = fZ + tex3D(Zdef0_tex,fXn, fYn, fZn);
// find location on the detector
fU = fCu.w + fXs * fCu.x + fYs * fCu.y + fZs * fCu.z;
fV = fCv.w + fXs * fCv.x + fYs * fCv.y + fZs * fCv.z;
for (int idx = 0; idx < ZSIZE; ++idx) {
// get bilinear interpolation back to non-shifted coordinates
Z[idx] += tex3D(gT_par3DProjTexture, fU, fAngle, fV);
// TODO: check if approximation that deformation is constant for Z block is valid !!
fU += fCu.z;
fV += fCv.z;
}
*/
// ARBITRARY DEFORMATIONS APPROXIMATION
fXn = X/limX; // normalized coordinates
fYn = Y/limY;
for (int idx = 0; idx < ZSIZE; ++idx) {
fZs = fZ + idx; // Z coordinate
fZn = (startZ+idx)/limZ; // normalized Z coordinate
// load deformed coordinates
if (!linear_deform_model){
fXs = fX + tex3D(Xdef0_tex,fXn, fYn, fZn);
fYs = fY + tex3D(Ydef0_tex,fXn, fYn, fZn);
fZs = fZs +tex3D(Zdef0_tex,fXn, fYn, fZn);
} else {
// deformated coordinates with linear interpolation
fXs = fX + (tex3D(Xdef0_tex,fXn, fYn, fZn) * (1-angle_ratio) + angle_ratio*tex3D(Xdef1_tex,fXn, fYn, fZn));
fYs = fY + (tex3D(Ydef0_tex,fXn, fYn, fZn) * (1-angle_ratio) + angle_ratio*tex3D(Ydef1_tex,fXn, fYn, fZn));
fZs = fZs +(tex3D(Zdef0_tex,fXn, fYn, fZn) * (1-angle_ratio) + angle_ratio*tex3D(Zdef1_tex,fXn, fYn, fZn));
}
// find location on the detector
fU = fCu.w + fXs * fCu.x + fYs * fCu.y + fZs * fCu.z;
fV = fCv.w + fXs * fCv.x + fYs * fCv.y + fZs * fCv.z;
// get bilinear interpolation back to non-shifted coordinates
Z[idx] += tex3D(gT_par3DProjTexture, fU, fAngle, fV);
// TODO: check if approximation that deformation is constant for Z block is valid !!
fU += fCu.z;
fV += fCv.z;
}
} else {
fU = fCu.w + fX * fCu.x + fY * fCu.y + fZ * fCu.z;
fV = fCv.w + fX * fCv.x + fY * fCv.y + fZ * fCv.z;
for (int idx = 0; idx < ZSIZE; ++idx) {
Z[idx] += tex3D(gT_par3DProjTexture, fU, fAngle, fV);
fU += fCu.z;
fV += fCv.z;
}
}
}
int endZ = ZSIZE;
if (endZ > dims.iVolZ - startZ)
endZ = dims.iVolZ - startZ;
for(int i=0; i < endZ; i++)
volData[((startZ+i)*dims.iVolY+Y)*volPitch+X] += Z[i] * fOutputScale;
}
// supersampling version
__global__ void dev_par3D_BP_SS(void* D_volData, unsigned int volPitch, int startAngle, int angleOffset, const SDimensions3D dims, float fOutputScale)
{
float* volData = (float*)D_volData;
int endAngle = startAngle + g_anglesPerBlock;
if (endAngle > dims.iProjAngles - angleOffset)
endAngle = dims.iProjAngles - angleOffset;
// threadIdx: x = rel x
// y = rel y
// blockIdx: x = x + y
// y = z
// TO TRY: precompute part of detector intersection formulas in shared mem?
// TO TRY: inner loop over z, gather ray values in shared mem
const int X = blockIdx.x % ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockX + threadIdx.x;
const int Y = blockIdx.x / ((dims.iVolX+g_volBlockX-1)/g_volBlockX) * g_volBlockY + threadIdx.y;
if (X >= dims.iVolX)
return;
if (Y >= dims.iVolY)
return;
const int startZ = blockIdx.y * g_volBlockZ;
int endZ = startZ + g_volBlockZ;
if (endZ > dims.iVolZ)
endZ = dims.iVolZ;
float fX = X - 0.5f*dims.iVolX + 0.5f - 0.5f + 0.5f/dims.iRaysPerVoxelDim;
float fY = Y - 0.5f*dims.iVolY + 0.5f - 0.5f + 0.5f/dims.iRaysPerVoxelDim;
float fZ = startZ - 0.5f*dims.iVolZ + 0.5f - 0.5f + 0.5f/dims.iRaysPerVoxelDim;
const float fSubStep = 1.0f/dims.iRaysPerVoxelDim;
fOutputScale /= (dims.iRaysPerVoxelDim*dims.iRaysPerVoxelDim*dims.iRaysPerVoxelDim);
for (int Z = startZ; Z < endZ; ++Z, fZ += 1.0f)
{
float fVal = 0.0f;
float fAngle = startAngle + angleOffset + 0.5f;
for (int angle = startAngle; angle < endAngle; ++angle, fAngle += 1.0f)
{
const float fCux = gC_C[8*angle+0];
const float fCuy = gC_C[8*angle+1];
const float fCuz = gC_C[8*angle+2];
const float fCuc = gC_C[8*angle+3];
const float fCvx = gC_C[8*angle+4];
const float fCvy = gC_C[8*angle+5];
const float fCvz = gC_C[8*angle+6];
const float fCvc = gC_C[8*angle+7];
float fXs = fX;
for (int iSubX = 0; iSubX < dims.iRaysPerVoxelDim; ++iSubX) {
float fYs = fY;
for (int iSubY = 0; iSubY < dims.iRaysPerVoxelDim; ++iSubY) {
float fZs = fZ;
for (int iSubZ = 0; iSubZ < dims.iRaysPerVoxelDim; ++iSubZ) {
const float fU = fCuc + fXs * fCux + fYs * fCuy + fZs * fCuz;
const float fV = fCvc + fXs * fCvx + fYs * fCvy + fZs * fCvz;
fVal += tex3D(gT_par3DProjTexture, fU, fAngle, fV);
fZs += fSubStep;
}
fYs += fSubStep;
}
fXs += fSubStep;
}
}
volData[(Z*dims.iVolY+Y)*volPitch+X] += fVal * fOutputScale;
}
}
bool Par3DBP_Array(cudaPitchedPtr D_volumeData,
const SDimensions3D& dims, const SPar3DProjection* angles,
float fOutputScale, bool use_deform, bool linear_deform_model)
{
for (unsigned int th = 0; th < dims.iProjAngles; th += g_MaxAngles) {
unsigned int angleCount = g_MaxAngles;
if (th + angleCount > dims.iProjAngles)
angleCount = dims.iProjAngles - th;
// transfer angles to constant memory
float* tmp = new float[8*dims.iProjAngles];
// NB: We increment angles at the end of the loop body.
// TODO: Use functions from dims3d.cu for this:
#define TRANSFER_TO_CONSTANT(expr,name) do { for (unsigned int i = 0; i < angleCount; ++i) tmp[8*i + name] = (expr) ; } while (0)
#define DENOM (angles[i].fRayX*angles[i].fDetUY*angles[i].fDetVZ - angles[i].fRayX*angles[i].fDetUZ*angles[i].fDetVY - angles[i].fRayY*angles[i].fDetUX*angles[i].fDetVZ + angles[i].fRayY*angles[i].fDetUZ*angles[i].fDetVX + angles[i].fRayZ*angles[i].fDetUX*angles[i].fDetVY - angles[i].fRayZ*angles[i].fDetUY*angles[i].fDetVX)
TRANSFER_TO_CONSTANT( ( - (angles[i].fRayY*angles[i].fDetVZ - angles[i].fRayZ*angles[i].fDetVY)) / DENOM , 0 );
TRANSFER_TO_CONSTANT( ( (angles[i].fRayX*angles[i].fDetVZ - angles[i].fRayZ*angles[i].fDetVX)) / DENOM , 1 );
TRANSFER_TO_CONSTANT( (- (angles[i].fRayX*angles[i].fDetVY - angles[i].fRayY*angles[i].fDetVX) ) / DENOM , 2 );
TRANSFER_TO_CONSTANT( (-(angles[i].fDetSY*angles[i].fDetVZ - angles[i].fDetSZ*angles[i].fDetVY)*angles[i].fRayX + (angles[i].fRayY*angles[i].fDetVZ - angles[i].fRayZ*angles[i].fDetVY)*angles[i].fDetSX - (angles[i].fRayY*angles[i].fDetSZ - angles[i].fRayZ*angles[i].fDetSY)*angles[i].fDetVX) / DENOM , 3 );
TRANSFER_TO_CONSTANT( ((angles[i].fRayY*angles[i].fDetUZ - angles[i].fRayZ*angles[i].fDetUY) ) / DENOM , 4 );
TRANSFER_TO_CONSTANT( (- (angles[i].fRayX*angles[i].fDetUZ - angles[i].fRayZ*angles[i].fDetUX) ) / DENOM , 5 );
TRANSFER_TO_CONSTANT( ((angles[i].fRayX*angles[i].fDetUY - angles[i].fRayY*angles[i].fDetUX) ) / DENOM , 6 );
TRANSFER_TO_CONSTANT( ((angles[i].fDetSY*angles[i].fDetUZ - angles[i].fDetSZ*angles[i].fDetUY)*angles[i].fRayX - (angles[i].fRayY*angles[i].fDetUZ - angles[i].fRayZ*angles[i].fDetUY)*angles[i].fDetSX + (angles[i].fRayY*angles[i].fDetSZ - angles[i].fRayZ*angles[i].fDetSY)*angles[i].fDetUX ) / DENOM , 7 );
#undef TRANSFER_TO_CONSTANT
#undef DENOM
cudaMemcpyToSymbol(gC_C, tmp, angleCount*8*sizeof(float), 0, cudaMemcpyHostToDevice);
delete[] tmp;
checkLastError("after cudaMemcpyToSymbol");
dim3 dimBlock(g_volBlockX, g_volBlockY);
dim3 dimGrid(((dims.iVolX+g_volBlockX-1)/g_volBlockX)*((dims.iVolY+g_volBlockY-1)/g_volBlockY), (dims.iVolZ+g_volBlockZ-1)/g_volBlockZ);
// timeval t;
// tic(t);
for (unsigned int i = 0; i < angleCount; i += g_anglesPerBlock) {
// printf("Calling BP: %d, %dx%d, %dx%d to %p\n", i, dimBlock.x, dimBlock.y, dimGrid.x, dimGrid.y, (void*)D_volumeData.ptr);
if (dims.iRaysPerVoxelDim == 1)
dev_par3D_BP<<<dimGrid, dimBlock>>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), i, th, dims, fOutputScale, use_deform, linear_deform_model);
else
dev_par3D_BP_SS<<<dimGrid, dimBlock>>>(D_volumeData.ptr, D_volumeData.pitch/sizeof(float), i, th, dims, fOutputScale);
}
cudaTextForceKernelsCompletion();
checkLastError("after cudaTextForceKernelsCompletion");
angles = angles + angleCount;
// printf("%f\n", toc(t));
}
return true;
}
bool Par3DBP(cudaPitchedPtr D_volumeData,
cudaPitchedPtr D_projData,
const SDimensions3D& dims, const SPar3DProjection* angles,
float fOutputScale, DeformField DF)
{
// transfer projections to array
checkLastError("before allocateVolumeArray");
cudaArray* cuArray = allocateProjectionArray(dims);
checkLastError("after allocateVolumeArray");
transferProjectionsToArray(D_projData, cuArray, dims);
checkLastError("after transferProjectionsToArray");
bindDataTexture(cuArray, gT_par3DProjTexture, cudaAddressModeBorder, false);
checkLastError("after bindProjDataTexture");
cudaArray * cuArrX0, *cuArrY0, *cuArrZ0, *cuArrX1, *cuArrY1, *cuArrZ1 ;
if (DF.use_deform) {
// mexPrintf("transferDeformationToArray\n");
cuArrX0 = transferDeformationToArray(DF.X0);
cuArrY0 = transferDeformationToArray(DF.Y0);
cuArrZ0 = transferDeformationToArray(DF.Z0);
bindDataTexture(cuArrX0, Xdef0_tex,cudaAddressModeClamp, true);
bindDataTexture(cuArrY0, Ydef0_tex,cudaAddressModeClamp, true);
bindDataTexture(cuArrZ0, Zdef0_tex,cudaAddressModeClamp, true);
if (DF.use_linear_model) {
cuArrX1 = transferDeformationToArray(DF.X1);
cuArrY1 = transferDeformationToArray(DF.Y1);
cuArrZ1 = transferDeformationToArray(DF.Z1);
bindDataTexture(cuArrX1, Xdef1_tex,cudaAddressModeClamp, true);
bindDataTexture(cuArrY1, Ydef1_tex,cudaAddressModeClamp, true);
bindDataTexture(cuArrZ1, Zdef1_tex,cudaAddressModeClamp, true);
}
}
bool ret = Par3DBP_Array(D_volumeData, dims, angles, fOutputScale, DF.use_deform, DF.use_linear_model);
checkLastError("after Par3DBP_Array");
cudaUnbindTexture(gT_par3DProjTexture);
checkLastError("after cudaUnbindTexture");
cudaFreeArray(cuArray);
checkLastError("after cudaFreeArray");
if (DF.use_deform) {
cudaFreeArray(cuArrX0);
cudaFreeArray(cuArrY0);
cudaFreeArray(cuArrZ0);
cudaUnbindTexture(Xdef0_tex);
cudaUnbindTexture(Ydef0_tex);
cudaUnbindTexture(Zdef0_tex);
if (DF.use_linear_model) {
cudaFreeArray(cuArrX1);
cudaFreeArray(cuArrY1);
cudaFreeArray(cuArrZ1);
cudaUnbindTexture(Xdef1_tex);
cudaUnbindTexture(Ydef1_tex);
cudaUnbindTexture(Zdef1_tex);
}
checkLastError("unbind deforms");
}
return ret;
}
}
+47
View File
@@ -0,0 +1,47 @@
/*
-----------------------------------------------------------------------
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$
*/
#ifndef _CUDA_PAR3D_BP_H
#define _CUDA_PAR3D_BP_H
namespace astraCUDA3d {
_AstraExport bool Par3DBP_Array(cudaPitchedPtr D_volumeData,
cudaArray *D_projArray,
const SDimensions3D& dims, const SPar3DProjection* angles,
float fOutputScale);
_AstraExport bool Par3DBP(cudaPitchedPtr D_volumeData,
cudaPitchedPtr D_projData,
const SDimensions3D& dims, const SPar3DProjection* angles,
float fOutputScale, DeformField DF);
}
#endif
+929
View File
@@ -0,0 +1,929 @@
/*
*-----------------------------------------------------------------------*
|                                                                       |
|  Except where otherwise noted, this work is licensed under a          |
|  Creative Commons Attribution-NonCommercial-ShareAlike 4.0            |
|  International (CC BY-NC-SA 4.0) license.                             |
|                                                                       |
|  Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch)    |
|                                                                       |
|      Author: CXS group, PSI  |
*-----------------------------------------------------------------------*
You may use this code with the following provisions:
If the code is fully or partially redistributed, or rewritten in another
computing language this notice should be included in the redistribution.
If this code, or subfunctions or parts of it, is used for research in a
publication or if it is fully or partially rewritten for another
computing language the authors and institution should be acknowledged
in written form in the publication: “Data processing was carried out
using the “cSAXS matlab package” developed by the CXS group,
Paul Scherrer Institut, Switzerland.”
Variations on the latter text can be incorporated upon discussion with
the CXS group if needed to more specifically reflect the use of the package
for the published work.
A publication that focuses on describing features, or parameters, that
are already existing in the code should be first discussed with the
authors.
This code and subroutines are part of a continuous development, they
are provided “as they are” without guarantees or liability on part
of PSI or the authors. It is the user responsibility to ensure its
proper use and the correctness of the results.
-----------------------------------------------------------------------
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 <iostream>
#include <list>
#include <cuda.h>
#include "util3d.h"
#include "mex.h"
#include "gpu/mxGPUArray.h"
#ifdef STANDALONE
#include "testutil.h"
#endif
#include "dims3d.h"
typedef texture<float, 3, cudaReadModeElementType> texture3D;
static texture3D gT_par3DVolumeTexture, Xdef0_tex, Ydef0_tex, Zdef0_tex, Xdef1_tex, Ydef1_tex, Zdef1_tex;
#define MAX(x,y) (x>y?x:y);
#define MIN(x,y) (x<y?x:y);
namespace astraCUDA3d {
static const unsigned int g_anglesPerBlock = 4;
// thickness of the slices we're splitting the volume up into
static const unsigned int g_blockSlices = 32;
static const unsigned int g_detBlockU = 32;
static const unsigned int g_detBlockV = 32;
static const unsigned g_MaxAngles = 1024;
__constant__ float gC_RayX[g_MaxAngles];
__constant__ float gC_RayY[g_MaxAngles];
__constant__ float gC_RayZ[g_MaxAngles];
__constant__ float gC_DetSX[g_MaxAngles];
__constant__ float gC_DetSY[g_MaxAngles];
__constant__ float gC_DetSZ[g_MaxAngles];
__constant__ float gC_DetUX[g_MaxAngles];
__constant__ float gC_DetUY[g_MaxAngles];
__constant__ float gC_DetUZ[g_MaxAngles];
__constant__ float gC_DetVX[g_MaxAngles];
__constant__ float gC_DetVY[g_MaxAngles];
__constant__ float gC_DetVZ[g_MaxAngles];
//__constant__ uint8_T gC_use_deform[1];
void __global__ SetVal(float const * const A, float * const B, int const N)
{
/* Calculate the global linear index, assuming a 1-d grid. */
int const i = blockDim.x * blockIdx.x + threadIdx.x;
if (i < N) {
B[i] = A[i];
}
}
// x=0, y=1, z=2
struct DIR_X {
__device__ float nSlices(const SDimensions3D& dims) const { return dims.iVolX; }
__device__ float nDim1(const SDimensions3D& dims) const { return dims.iVolY; }
__device__ float nDim2(const SDimensions3D& dims) const { return dims.iVolZ; }
__device__ float c0(float x, float y, float z) const { return x; }
__device__ float c1(float x, float y, float z) const { return y; }
__device__ float c2(float x, float y, float z) const { return z; }
__device__ float tex(float f0, float f1, float f2) const { return tex3D(gT_par3DVolumeTexture, f0, f1, f2); }
__device__ float texD0x(float f0, float f1, float f2) const { return tex3D(Xdef0_tex, f0, f1, f2); }
__device__ float texD0y(float f0, float f1, float f2) const { return tex3D(Ydef0_tex, f0, f1, f2); }
__device__ float texD0z(float f0, float f1, float f2) const { return tex3D(Zdef0_tex, f0, f1, f2); }
__device__ float texD1x(float f0, float f1, float f2) const { return tex3D(Xdef1_tex, f0, f1, f2); }
__device__ float texD1y(float f0, float f1, float f2) const { return tex3D(Ydef1_tex, f0, f1, f2); }
__device__ float texD1z(float f0, float f1, float f2) const { return tex3D(Zdef1_tex, f0, f1, f2); }
__device__ float x(float f0, float f1, float f2) const { return f0; }
__device__ float y(float f0, float f1, float f2) const { return f1; }
__device__ float z(float f0, float f1, float f2) const { return f2; }
__device__ float offx(const SDimensions3D& dims) const { return dims.iProjU*0.5f; }
__device__ float offy(const SDimensions3D& dims) const { return dims.iProjV*0.5f; }
};
// y=0, x=1, z=2
struct DIR_Y {
__device__ float nSlices(const SDimensions3D& dims) const { return dims.iVolY; }
__device__ float nDim1(const SDimensions3D& dims) const { return dims.iVolX; }
__device__ float nDim2(const SDimensions3D& dims) const { return dims.iVolZ; }
__device__ float c0(float x, float y, float z) const { return y; }
__device__ float c1(float x, float y, float z) const { return x; }
__device__ float c2(float x, float y, float z) const { return z; }
__device__ float tex(float f0, float f1, float f2) const { return tex3D(gT_par3DVolumeTexture, f1, f0, f2); }
__device__ float texD0x(float f0, float f1, float f2) const { return tex3D(Ydef0_tex, f1, f0, f2); }
__device__ float texD0y(float f0, float f1, float f2) const { return tex3D(Xdef0_tex, f1, f0, f2); }
__device__ float texD0z(float f0, float f1, float f2) const { return tex3D(Zdef0_tex, f1, f0, f2); }
__device__ float texD1x(float f0, float f1, float f2) const { return tex3D(Ydef1_tex, f1, f0, f2); }
__device__ float texD1y(float f0, float f1, float f2) const { return tex3D(Xdef1_tex, f1, f0, f2); }
__device__ float texD1z(float f0, float f1, float f2) const { return tex3D(Zdef1_tex, f1, f0, f2); }
__device__ float x(float f0, float f1, float f2) const { return f1; }
__device__ float y(float f0, float f1, float f2) const { return f0; }
__device__ float z(float f0, float f1, float f2) const { return f2; }
__device__ float offx(const SDimensions3D& dims) const { return dims.iProjU*0.5f; }
__device__ float offy(const SDimensions3D& dims) const { return dims.iProjV*0.5f; }
};
// z=0, x=1, y=2
struct DIR_Z {
__device__ float nSlices(const SDimensions3D& dims) const { return dims.iVolZ; }
__device__ float nDim1(const SDimensions3D& dims) const { return dims.iVolX; }
__device__ float nDim2(const SDimensions3D& dims) const { return dims.iVolY; }
__device__ float c0(float x, float y, float z) const { return z; }
__device__ float c1(float x, float y, float z) const { return x; }
__device__ float c2(float x, float y, float z) const { return y; }
__device__ float tex(float f0, float f1, float f2) const { return tex3D(gT_par3DVolumeTexture, f1, f2, f0); }
__device__ float texD0x(float f0, float f1, float f2) const { return tex3D(Zdef0_tex, f1, f2, f0); }
__device__ float texD0y(float f0, float f1, float f2) const { return tex3D(Xdef0_tex, f1, f2, f0); }
__device__ float texD0z(float f0, float f1, float f2) const { return tex3D(Ydef0_tex, f1, f2, f0); }
__device__ float texD1x(float f0, float f1, float f2) const { return tex3D(Zdef1_tex, f1, f2, f0); }
__device__ float texD1y(float f0, float f1, float f2) const { return tex3D(Xdef1_tex, f1, f2, f0); }
__device__ float texD1z(float f0, float f1, float f2) const { return tex3D(Ydef1_tex, f1, f2, f0); }
__device__ float x(float f0, float f1, float f2) const { return f1; }
__device__ float y(float f0, float f1, float f2) const { return f2; }
__device__ float z(float f0, float f1, float f2) const { return f0; }
__device__ float offx(const SDimensions3D& dims) const { return dims.iProjU*0.5f; }
__device__ float offy(const SDimensions3D& dims) const { return dims.iProjV*0.5f; }
};
// threadIdx: x = u detector
// y = relative angle
// blockIdx: x = u/v detector
// y = angle block
template<class COORD>
__global__ void par3D_FP_t(float* D_projData, unsigned int projPitch,
unsigned int startSlice,
unsigned int startAngle, unsigned int endAngle,
const SDimensions3D dims, float fOutputScale, const bool use_deform, const bool linear_deform_model)
{
COORD c;
int angle = startAngle + blockIdx.y * g_anglesPerBlock + threadIdx.y;
if (angle >= endAngle)
return;
const float fRayX = gC_RayX[angle];
const float fRayY = gC_RayY[angle];
const float fRayZ = gC_RayZ[angle];
const float fDetUX = gC_DetUX[angle];
const float fDetUY = gC_DetUY[angle];
const float fDetUZ = gC_DetUZ[angle];
const float fDetVX = gC_DetVX[angle];
const float fDetVY = gC_DetVY[angle];
const float fDetVZ = gC_DetVZ[angle];
const float fDetSX = gC_DetSX[angle] + 0.5f * fDetUX + 0.5f * fDetVX;
const float fDetSY = gC_DetSY[angle] + 0.5f * fDetUY + 0.5f * fDetVY;
const float fDetSZ = gC_DetSZ[angle] + 0.5f * fDetUZ + 0.5f * fDetVZ;
if (c.c0(fRayX, fRayY, fRayZ) == 0)
return;
const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x;
if (detectorU >= dims.iProjU)
return;
const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV;
int endDetectorV = startDetectorV + g_detBlockV;
if (endDetectorV > dims.iProjV)
endDetectorV = dims.iProjV;
int endSlice = startSlice + g_blockSlices;
if (endSlice > c.nSlices(dims))
endSlice = c.nSlices(dims);
// FIXME
/*if (endSlice < startSlice - 1)
return;*/
float angle_ratio = (float)angle / (float)dims.iProjAngles ;
for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV)
{
/* Trace ray in direction Ray to (detectorU,detectorV) from */
/* X = startSlice to X = endSlice */
const float fDetX = fDetSX + (detectorU*fDetUX + detectorV*fDetVX);
const float fDetY = fDetSY + (detectorU*fDetUY + detectorV*fDetVY);
const float fDetZ = fDetSZ + (detectorU*fDetUZ + detectorV*fDetVZ);
/* (x) ( 1) ( 0) */
/* ray: (y) = (ay) * x + (by) */
/* (z) (az) (bz) */
const float a1 = c.c1(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ);
const float a2 = c.c2(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ);
const float b1 = c.c1(fDetX,fDetY,fDetZ) - a1 * c.c0(fDetX,fDetY,fDetZ);
const float b2 = c.c2(fDetX,fDetY,fDetZ) - a2 * c.c0(fDetX,fDetY,fDetZ);
const float fDistCorr = sqrt(a1*a1+a2*a2+1.0f) * fOutputScale;
float fVal = 0.0f;
//float f0 = startSlice + 0.5f;
//float f1 = a1 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b1 + 0.5f*c.nDim1(dims) - 0.5f + 0.5f;
//float f2 = a2 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b2 + 0.5f*c.nDim2(dims) - 0.5f + 0.5f;
bool is_inside;
int lim0, lim1, lim2;
lim0 = c.nSlices(dims);
lim1 = c.nDim1(dims);
lim2 = c.nDim2(dims);
const float offset = 0.5*lim0;
// calculate minimal distance needed to get the subprojection, important for laminography and large projection size
int startSlice_tmp = startSlice;
int endSlice_tmp = endSlice;
if (a1 > 0)
{
startSlice_tmp = MAX(startSlice_tmp, floor((-0.5*lim1-b1-0.5f)/a1+offset-1.0f));
endSlice_tmp = MIN(endSlice_tmp, ceil((+0.5*lim1-b1+0.5f)/a1+offset+1.0f));
}
else if (a1 < 0)
{
startSlice_tmp = MAX(startSlice_tmp, floor((+0.5*lim1-b1+0.5f)/a1+offset-1.0f));
endSlice_tmp = MIN(endSlice_tmp, ceil((-0.5*lim1-b1-0.5f)/a1+offset+1.0f));
}
if (a2 > 0)
{
startSlice_tmp = MAX(startSlice_tmp, floor((-0.5*lim2-b2-0.5f)/a2+offset-1.0f));
endSlice_tmp = MIN(endSlice_tmp, ceil((+0.5*lim2-b2+0.5f)/a2+offset+1.0f));
}
else if (a2 < 0)
{
startSlice_tmp = MAX(startSlice_tmp, floor((+0.5*lim2-b2+0.5f)/a2+offset-1.0f));
endSlice_tmp = MIN(endSlice_tmp, ceil((-0.5*lim2-b2-0.5f)/a2+offset+1.0f));
}
endSlice_tmp = MIN(endSlice_tmp, endSlice);
endSlice_tmp = MAX(endSlice_tmp, 0);
startSlice_tmp = MAX(startSlice_tmp, startSlice);
startSlice_tmp = MIN(startSlice_tmp, endSlice_tmp);
float f0 = startSlice_tmp + 0.5f;
float f1 = a1 * (startSlice_tmp - offset+0.5f) + b1 + 0.5f*c.nDim1(dims);
float f2 = a2 * (startSlice_tmp - offset+0.5f) + b2 + 0.5f*c.nDim2(dims);
float f0s, f1s, f2s; // shifted coordinates
float f0n, f1n, f2n; // normalized coordinates
// 87% of the execution time
for (int s = startSlice_tmp; s < endSlice_tmp; ++s)
{
if (use_deform) {
f0n = f0/lim0; // normalized coordinates
f1n = f1/lim1;
f2n = f2/lim2;
// load deformed coordinates
if (!linear_deform_model) {
f0s = f0 - c.texD0x(f0n, f1n, f2n);
f1s = f1 - c.texD0y(f0n, f1n, f2n);
f2s = f2 - c.texD0z(f0n, f1n, f2n);
} else {
f0s = f0 - (c.texD0x(f0n, f1n, f2n) * (1-angle_ratio) + (angle_ratio)*c.texD1x(f0n, f1n, f2n));
f1s = f1 - (c.texD0y(f0n, f1n, f2n) * (1-angle_ratio) + (angle_ratio)*c.texD1y(f0n, f1n, f2n));
f2s = f2 - (c.texD0z(f0n, f1n, f2n) * (1-angle_ratio) + (angle_ratio)*c.texD1z(f0n, f1n, f2n));
}
// get trilinear interpolation in the shifted coordinates
fVal += c.tex(f0s, f1s, f2s);
} else {
is_inside = (f0 > 0 && f1 > 0 && f2 > 0 && f0 < lim0 && f1 < lim1 && f2 < lim2 );
// fVal += (is_inside ? c.tex(f0, f1, f2) : 0); // skip textures on boundaries
//fVal += c.tex(f0, f1, f2) == 0;
//fVal += is_inside == 0;
fVal += c.tex(f0, f1, f2); // fastest seems to be let texture memory to handle boundaries
}
// move to the next pixel
f0 += 1.0f;
f1 += a1;
f2 += a2;
}
fVal *= fDistCorr;
// !! 10% of the execution time
//D_projData[(detectorV*dims.iProjAngles + angle)*projPitch + detectorU] += fVal;
atomicAdd(&D_projData[(detectorV*dims.iProjAngles + angle)*projPitch + detectorU], fVal);
}
}
// Supersampling version
template<class COORD>
__global__ void par3D_FP_SS_t(float* D_projData, unsigned int projPitch,
unsigned int startSlice,
unsigned int startAngle, unsigned int endAngle,
const SDimensions3D dims, float fOutputScale)
{
COORD c;
int angle = startAngle + blockIdx.y * g_anglesPerBlock + threadIdx.y;
if (angle >= endAngle)
return;
const float fRayX = gC_RayX[angle];
const float fRayY = gC_RayY[angle];
const float fRayZ = gC_RayZ[angle];
const float fDetUX = gC_DetUX[angle];
const float fDetUY = gC_DetUY[angle];
const float fDetUZ = gC_DetUZ[angle];
const float fDetVX = gC_DetVX[angle];
const float fDetVY = gC_DetVY[angle];
const float fDetVZ = gC_DetVZ[angle];
const float fDetSX = gC_DetSX[angle] + 0.5f * fDetUX + 0.5f * fDetVX;
const float fDetSY = gC_DetSY[angle] + 0.5f * fDetUY + 0.5f * fDetVY;
const float fDetSZ = gC_DetSZ[angle] + 0.5f * fDetUZ + 0.5f * fDetVZ;
const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x;
const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV;
int endDetectorV = startDetectorV + g_detBlockV;
if (endDetectorV > dims.iProjV)
endDetectorV = dims.iProjV;
int endSlice = startSlice + g_blockSlices;
if (endSlice > c.nSlices(dims))
endSlice = c.nSlices(dims);
const float fSubStep = 1.0f/dims.iRaysPerDetDim;
for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV)
{
float fV = 0.0f;
float fdU = detectorU - 0.5f + 0.5f*fSubStep;
for (int iSubU = 0; iSubU < dims.iRaysPerDetDim; ++iSubU, fdU+=fSubStep) {
float fdV = detectorV - 0.5f + 0.5f*fSubStep;
for (int iSubV = 0; iSubV < dims.iRaysPerDetDim; ++iSubV, fdV+=fSubStep) {
/* Trace ray in direction Ray to (detectorU,detectorV) from */
/* X = startSlice to X = endSlice */
const float fDetX = fDetSX + fdU*fDetUX + fdV*fDetVX;
const float fDetY = fDetSY + fdU*fDetUY + fdV*fDetVY;
const float fDetZ = fDetSZ + fdU*fDetUZ + fdV*fDetVZ;
/* (x) ( 1) ( 0) */
/* ray: (y) = (ay) * x + (by) */
/* (z) (az) (bz) */
const float a1 = c.c1(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ);
const float a2 = c.c2(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ);
const float b1 = c.c1(fDetX,fDetY,fDetZ) - a1 * c.c0(fDetX,fDetY,fDetZ);
const float b2 = c.c2(fDetX,fDetY,fDetZ) - a2 * c.c0(fDetX,fDetY,fDetZ);
const float fDistCorr = sqrt(a1*a1+a2*a2+1.0f) * fOutputScale;
float fVal = 0.0f;
float f0 = startSlice + 0.5f;
float f1 = a1 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b1 + 0.5f*c.nDim1(dims) - 0.5f + 0.5f;
float f2 = a2 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b2 + 0.5f*c.nDim2(dims) - 0.5f + 0.5f;
for (int s = startSlice; s < endSlice; ++s)
{
fVal += c.tex(f0, f1, f2);
f0 += 1.0f;
f1 += a1 ;
// f2 += a2;
}
fVal *= fDistCorr;
fV += fVal;
}
}
D_projData[(detectorV*dims.iProjAngles+angle)*projPitch+detectorU] += fV / (dims.iRaysPerDetDim * dims.iRaysPerDetDim);
}
}
__device__ float dirWeights(float fX, float fN) {
if (fX <= -0.5f) // outside image on left
return 0.0f;
if (fX <= 0.5f) // half outside image on left
return (fX + 0.5f) * (fX + 0.5f);
if (fX <= fN - 0.5f) { // inside image
float t = fX + 0.5f - floorf(fX + 0.5f);
return 1; // t*t + (1 - t)*(1 - t);
}
if (fX <= fN + 0.5f) // half outside image on right
return (fN + 0.5f - fX) * (fN + 0.5f - fX);
return 0.0f; // outside image on right
}
template<class COORD>
__global__ void par3D_FP_SumSqW_t(float* D_projData, unsigned int projPitch,
unsigned int startSlice,
unsigned int startAngle, unsigned int endAngle,
const SDimensions3D dims, float fOutputScale)
{
COORD c;
int angle = startAngle + blockIdx.y * g_anglesPerBlock + threadIdx.y;
if (angle >= endAngle)
return;
const float fRayX = gC_RayX[angle];
const float fRayY = gC_RayY[angle];
const float fRayZ = gC_RayZ[angle];
const float fDetUX = gC_DetUX[angle];
const float fDetUY = gC_DetUY[angle];
const float fDetUZ = gC_DetUZ[angle];
const float fDetVX = gC_DetVX[angle];
const float fDetVY = gC_DetVY[angle];
const float fDetVZ = gC_DetVZ[angle];
const float fDetSX = gC_DetSX[angle] + 0.5f * fDetUX + 0.5f * fDetVX;
const float fDetSY = gC_DetSY[angle] + 0.5f * fDetUY + 0.5f * fDetVY;
const float fDetSZ = gC_DetSZ[angle] + 0.5f * fDetUZ + 0.5f * fDetVZ;
const int detectorU = (blockIdx.x%((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockU + threadIdx.x;
const int startDetectorV = (blockIdx.x/((dims.iProjU+g_detBlockU-1)/g_detBlockU)) * g_detBlockV;
int endDetectorV = startDetectorV + g_detBlockV;
if (endDetectorV > dims.iProjV)
endDetectorV = dims.iProjV;
int endSlice = startSlice + g_blockSlices;
if (endSlice > c.nSlices(dims))
endSlice = c.nSlices(dims);
for (int detectorV = startDetectorV; detectorV < endDetectorV; ++detectorV)
{
/* Trace ray in direction Ray to (detectorU,detectorV) from */
/* X = startSlice to X = endSlice */
const float fDetX = fDetSX + detectorU*fDetUX + detectorV*fDetVX;
const float fDetY = fDetSY + detectorU*fDetUY + detectorV*fDetVY;
const float fDetZ = fDetSZ + detectorU*fDetUZ + detectorV*fDetVZ;
/* (x) ( 1) ( 0) */
/* ray: (y) = (ay) * x + (by) */
/* (z) (az) (bz) */
const float a1 = c.c1(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ);
const float a2 = c.c2(fRayX,fRayY,fRayZ) / c.c0(fRayX,fRayY,fRayZ);
const float b1 = c.c1(fDetX,fDetY,fDetZ) - a1 * c.c0(fDetX,fDetY,fDetZ);
const float b2 = c.c2(fDetX,fDetY,fDetZ) - a2 * c.c0(fDetX,fDetY,fDetZ);
const float fDistCorr = sqrt(a1*a1+a2*a2+1.0f) * fOutputScale;
float fVal = 0.0f;
float f0 = startSlice + 0.5f;
float f1 = a1 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b1 + 0.5f*c.nDim1(dims) - 0.5f + 0.5f;
float f2 = a2 * (startSlice - 0.5f*c.nSlices(dims) + 0.5f) + b2 + 0.5f*c.nDim2(dims) - 0.5f + 0.5f;
for (int s = startSlice; s < endSlice; ++s)
{
fVal += dirWeights(f1, c.nDim1(dims)) * dirWeights(f2, c.nDim2(dims)) * fDistCorr * fDistCorr;
f0 += 1.0f;
f1 += a1;
f2 += a2;
}
D_projData[(detectorV*dims.iProjAngles+angle)*projPitch+detectorU] += fVal;
}
}
// Supersampling version
// TODO
bool Par3DFP_Array_internal(cudaPitchedPtr D_projData,
const SDimensions3D& dims, unsigned int angleCount, const SPar3DProjection* angles,
float fOutputScale, const bool use_deform, const bool linear_deform_model)
{
// transfer angles to constant memory
float* tmp = new float[dims.iProjAngles];
#define TRANSFER_TO_CONSTANT(name) do { for (unsigned int i = 0; i < angleCount; ++i) tmp[i] = (float)angles[i].f##name ; cudaMemcpyToSymbol(gC_##name, tmp, angleCount*sizeof(float), 0, cudaMemcpyHostToDevice); } while (0)
TRANSFER_TO_CONSTANT(RayX);
TRANSFER_TO_CONSTANT(RayY);
TRANSFER_TO_CONSTANT(RayZ);
TRANSFER_TO_CONSTANT(DetSX);
TRANSFER_TO_CONSTANT(DetSY);
TRANSFER_TO_CONSTANT(DetSZ);
TRANSFER_TO_CONSTANT(DetUX);
TRANSFER_TO_CONSTANT(DetUY);
TRANSFER_TO_CONSTANT(DetUZ);
TRANSFER_TO_CONSTANT(DetVX);
TRANSFER_TO_CONSTANT(DetVY);
TRANSFER_TO_CONSTANT(DetVZ);
#undef TRANSFER_TO_CONSTANT
delete[] tmp;
std::list<cudaStream_t> streams;
dim3 dimBlock(g_detBlockU, g_anglesPerBlock); // region size, angles
// Run over all angles, grouping them into groups of the same
// orientation (roughly horizontal vs. roughly vertical).
// Start a stream of grids for each such group.
unsigned int blockStart = 0;
unsigned int blockEnd = 0;
int blockDirection = 0;
for (unsigned int a = 0; a <= angleCount; ++a) {
int dir = -1;
if (a != dims.iProjAngles) {
float dX = fabsf(angles[a].fRayX);
float dY = fabsf(angles[a].fRayY);
float dZ = fabsf(angles[a].fRayZ);
if (dX >= dY && dX >= dZ)
dir = 0;
else if (dY >= dX && dY >= dZ)
dir = 1;
else
dir = 2;
}
if (a == angleCount || dir != blockDirection) {
// block done
blockEnd = a;
if (blockStart != blockEnd) {
dim3 dimGrid(
((dims.iProjU+g_detBlockU-1)/g_detBlockU)*((dims.iProjV+g_detBlockV-1)/g_detBlockV),
(blockEnd-blockStart+g_anglesPerBlock-1)/g_anglesPerBlock);
// TODO: check if we can't immediately
// destroy the stream after use
cudaStream_t stream;
cudaStreamCreate(&stream);
streams.push_back(stream);
//mexPrintf("angle block: %d to %d, %d (%dx%d, %dx%d)\n", blockStart, blockEnd, blockDirection, dimGrid.x, dimGrid.y, dimBlock.x, dimBlock.y);
//mexPrintf(" Nelements %i ", (dims.iProjU)*(dims.iProjV)*(dims.iProjAngles));
if (blockDirection == 0) {
for (unsigned int i = 0; i < dims.iVolX; i += g_blockSlices)
if (dims.iRaysPerDetDim == 1)
par3D_FP_t<DIR_X><<<dimGrid, dimBlock, 0, stream>>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale, use_deform, linear_deform_model);
else
par3D_FP_SS_t<DIR_X><<<dimGrid, dimBlock, 0, stream>>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale);
} else if (blockDirection == 1) {
for (unsigned int i = 0; i < dims.iVolY; i += g_blockSlices)
if (dims.iRaysPerDetDim == 1)
par3D_FP_t<DIR_Y><<<dimGrid, dimBlock, 0, stream>>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale, use_deform, linear_deform_model);
else
par3D_FP_SS_t<DIR_Y><<<dimGrid, dimBlock, 0, stream>>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale);
} else if (blockDirection == 2) {
for (unsigned int i = 0; i < dims.iVolZ; i += g_blockSlices)
if (dims.iRaysPerDetDim == 1)
par3D_FP_t<DIR_Z><<<dimGrid, dimBlock, 0, stream>>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale, use_deform, linear_deform_model);
else
par3D_FP_SS_t<DIR_Z><<<dimGrid, dimBlock, 0, stream>>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale);
}
}
blockDirection = dir;
blockStart = a;
}
}
cudaThreadSynchronize();
for (std::list<cudaStream_t>::iterator iter = streams.begin(); iter != streams.end(); ++iter)
cudaStreamDestroy(*iter);
streams.clear();
cudaTextForceKernelsCompletion();
return true;
}
bool Par3DFP(cudaPitchedPtr D_volumeData,
cudaPitchedPtr D_projData,
const SDimensions3D& dims, const SPar3DProjection* angles,
float fOutputScale, DeformField DF)
{
checkLastError("before allocateVolumeArray");
/*printFreeMemory();
mexPrintf("Allocate memory\n");*/
// transfer volume to array
if (dims.iVolX*dims.iVolY*dims.iVolZ * 4 > 1024e6)
{
mexPrintf("Volume exceeded maximal size of texture 1024MB \n");
return 1;
}
cudaArray* cuArray = allocateVolumeArray(dims);
//mexPrintf("Allocate memory done\n");
//printFreeMemory();
checkLastError("after allocateVolumeArray");
//mexPrintf("transferVolumeToArray\n");
transferVolumeToArray(D_volumeData, cuArray, dims);
checkLastError("after transferVolumeToArray\n \n ");
//printFreeMemory();
bindDataTexture(cuArray, gT_par3DVolumeTexture,cudaAddressModeBorder, false);
//mexPrintf("bindDataTexture done \n");
checkLastError("after bindDataTexture");
//printFreeMemory();
//mexPrintf("preoparation finieshe \n");
cudaArray * cuArrX0, *cuArrY0, *cuArrZ0, *cuArrX1, *cuArrY1, *cuArrZ1 ;
if (DF.use_deform) {
// mexPrintf("transferDeformationToArray\n");
cuArrX0 = transferDeformationToArray(DF.X0);
cuArrY0 = transferDeformationToArray(DF.Y0);
cuArrZ0 = transferDeformationToArray(DF.Z0);
bindDataTexture(cuArrX0, Xdef0_tex,cudaAddressModeClamp, true);
bindDataTexture(cuArrY0, Ydef0_tex,cudaAddressModeClamp, true);
bindDataTexture(cuArrZ0, Zdef0_tex,cudaAddressModeClamp, true);
if (DF.use_linear_model) {
cuArrX1 = transferDeformationToArray(DF.X1);
cuArrY1 = transferDeformationToArray(DF.Y1);
cuArrZ1 = transferDeformationToArray(DF.Z1);
bindDataTexture(cuArrX1, Xdef1_tex,cudaAddressModeClamp, true);
bindDataTexture(cuArrY1, Ydef1_tex,cudaAddressModeClamp, true);
bindDataTexture(cuArrZ1, Zdef1_tex,cudaAddressModeClamp, true);
}
}
bool ret;
// ONLY A LIMITED RANGE OF ANGLES IS AVAILIBLE INSIDE !!!!!
checkLastError("before allocateVolumeArray");
// 97% of time spent in Par3DFP_Array_internal
ret = Par3DFP_Array_internal(D_projData,
dims, dims.iProjAngles, angles,
fOutputScale, DF.use_deform, DF.use_linear_model);
checkLastError("after allocateVolumeArray");
cudaFreeArray(cuArray);
checkLastError("after cudaFreeArray");
// THIS WAS BUG IN ASTRA !!!!
cudaUnbindTexture(gT_par3DVolumeTexture);
checkLastError("cudaUnbindTexture");
if (DF.use_deform) {
cudaFreeArray(cuArrX0);
cudaFreeArray(cuArrY0);
cudaFreeArray(cuArrZ0);
cudaUnbindTexture(Xdef0_tex);
cudaUnbindTexture(Ydef0_tex);
cudaUnbindTexture(Zdef0_tex);
if (DF.use_linear_model) {
cudaFreeArray(cuArrX1);
cudaFreeArray(cuArrY1);
cudaFreeArray(cuArrZ1);
cudaUnbindTexture(Xdef1_tex);
cudaUnbindTexture(Ydef1_tex);
cudaUnbindTexture(Zdef1_tex);
}
checkLastError("unbind deforms");
}
return ret;
}
bool Par3DFP_SumSqW(cudaPitchedPtr D_volumeData,
cudaPitchedPtr D_projData,
const SDimensions3D& dims, const SPar3DProjection* angles,
float fOutputScale)
{
// transfer angles to constant memory
float* tmp = new float[dims.iProjAngles];
#define TRANSFER_TO_CONSTANT(name) do { for (unsigned int i = 0; i < dims.iProjAngles; ++i) tmp[i] = angles[i].f##name ; cudaMemcpyToSymbol(gC_##name, tmp, dims.iProjAngles*sizeof(float), 0, cudaMemcpyHostToDevice); } while (0)
TRANSFER_TO_CONSTANT(RayX);
TRANSFER_TO_CONSTANT(RayY);
TRANSFER_TO_CONSTANT(RayZ);
TRANSFER_TO_CONSTANT(DetSX);
TRANSFER_TO_CONSTANT(DetSY);
TRANSFER_TO_CONSTANT(DetSZ);
TRANSFER_TO_CONSTANT(DetUX);
TRANSFER_TO_CONSTANT(DetUY);
TRANSFER_TO_CONSTANT(DetUZ);
TRANSFER_TO_CONSTANT(DetVX);
TRANSFER_TO_CONSTANT(DetVY);
TRANSFER_TO_CONSTANT(DetVZ);
#undef TRANSFER_TO_CONSTANT
delete[] tmp;
std::list<cudaStream_t> streams;
dim3 dimBlock(g_detBlockU, g_anglesPerBlock); // region size, angles
// Run over all angles, grouping them into groups of the same
// orientation (roughly horizontal vs. roughly vertical).
// Start a stream of grids for each such group.
unsigned int blockStart = 0;
unsigned int blockEnd = 0;
int blockDirection = 0;
// timeval t;
// tic(t);
for (unsigned int a = 0; a <= dims.iProjAngles; ++a) {
int dir;
if (a != dims.iProjAngles) {
float dX = fabsf(angles[a].fRayX);
float dY = fabsf(angles[a].fRayY);
float dZ = fabsf(angles[a].fRayZ);
if (dX >= dY && dX >= dZ)
dir = 0;
else if (dY >= dX && dY >= dZ)
dir = 1;
else
dir = 2;
}
if (a == dims.iProjAngles || dir != blockDirection) {
// block done
blockEnd = a;
if (blockStart != blockEnd) {
dim3 dimGrid(
((dims.iProjU+g_detBlockU-1)/g_detBlockU)*((dims.iProjV+g_detBlockV-1)/g_detBlockV),
(blockEnd-blockStart+g_anglesPerBlock-1)/g_anglesPerBlock);
// TODO: check if we can't immediately
// destroy the stream after use
cudaStream_t stream;
cudaStreamCreate(&stream);
streams.push_back(stream);
//printf("angle block: %d to %d, %d (%dx%d, %dx%d)\n", blockStart, blockEnd, blockDirection, dimGrid.x, dimGrid.y, dimBlock.x, dimBlock.y);
if (blockDirection == 0) {
for (unsigned int i = 0; i < dims.iVolX; i += g_blockSlices)
if (dims.iRaysPerDetDim == 1)
par3D_FP_SumSqW_t<DIR_X><<<dimGrid, dimBlock, 0, stream>>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale);
else
#if 0
par3D_FP_SS_SumSqW_dirX<<<dimGrid, dimBlock, 0, stream>>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale);
#else
assert(false);
#endif
} else if (blockDirection == 1) {
for (unsigned int i = 0; i < dims.iVolY; i += g_blockSlices)
if (dims.iRaysPerDetDim == 1)
par3D_FP_SumSqW_t<DIR_Y><<<dimGrid, dimBlock, 0, stream>>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale);
else
#if 0
par3D_FP_SS_SumSqW_dirY<<<dimGrid, dimBlock, 0, stream>>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale);
#else
assert(false);
#endif
} else if (blockDirection == 2) {
for (unsigned int i = 0; i < dims.iVolZ; i += g_blockSlices)
if (dims.iRaysPerDetDim == 1)
par3D_FP_SumSqW_t<DIR_Z><<<dimGrid, dimBlock, 0, stream>>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale);
else
#if 0
par3D_FP_SS_SumSqW_dirZ<<<dimGrid, dimBlock, 0, stream>>>((float*)D_projData.ptr, D_projData.pitch/sizeof(float), i, blockStart, blockEnd, dims, fOutputScale);
#else
assert(false);
#endif
}
}
blockDirection = dir;
blockStart = a;
}
}
for (std::list<cudaStream_t>::iterator iter = streams.begin(); iter != streams.end(); ++iter)
cudaStreamDestroy(*iter);
streams.clear();
cudaTextForceKernelsCompletion();
// printf("%f\n", toc(t));
return true;
}
}
+51
View File
@@ -0,0 +1,51 @@
/*
-----------------------------------------------------------------------
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$
*/
#ifndef _CUDA_PAR3D_FP_H
#define _CUDA_PAR3D_FP_H
namespace astraCUDA3d {
_AstraExport bool Par3DFP_Array(cudaArray *D_volArray,
cudaPitchedPtr D_projData,
const SDimensions3D& dims, const SPar3DProjection* angles,
float fOutputScale);
_AstraExport bool Par3DFP(cudaPitchedPtr D_volumeData,
cudaPitchedPtr D_projData,
const SDimensions3D& dims, const SPar3DProjection* angles,
float fOutputScale, DeformField DF);
_AstraExport bool Par3DFP_SumSqW(cudaPitchedPtr D_volumeData,
cudaPitchedPtr D_projData,
const SDimensions3D& dims, const SPar3DProjection* angles,
float fOutputScale);
}
#endif
+8
View File
@@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// ConsoleApplication2.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"
// TODO: reference any additional headers you need in STDAFX.H
// and not in this file
+16
View File
@@ -0,0 +1,16 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
// TODO: reference additional headers your program requires here
+8
View File
@@ -0,0 +1,8 @@
#pragma once
// Including SDKDDKVer.h defines the highest available Windows platform.
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <SDKDDKVer.h>
+688
View File
@@ -0,0 +1,688 @@
/*
-----------------------------------------------------------------------
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;
}
}
+121
View File
@@ -0,0 +1,121 @@
/*
-----------------------------------------------------------------------
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 <cuda.h>
#include <driver_types.h>
#ifdef _MSC_VER
#ifdef DLL_EXPORTS
#define _AstraExport __declspec(dllexport)
#define EXPIMP_TEMPLATE
#else
#define _AstraExport __declspec(dllimport)
#define EXPIMP_TEMPLATE extern
#endif
#else
#define _AstraExport
#endif
//#include "dims.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define ASTRA_CUDA_ASSERT(err) do { if (err != cudaSuccess) { astraCUDA3d::reportCudaError(err); assert(err == cudaSuccess); } } while(0)
#ifndef _CUDA_UTIL3D_H
#define _CUDA_UTIL3D_H
#include <cuda.h>
#include "dims3d.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
//#include "../2d/util.h"
namespace astraCUDA3d {
typedef texture<float, 3, cudaReadModeElementType> texture3D;
cudaPitchedPtr allocateVolumeData(const SDimensions3D& dims);
cudaPitchedPtr allocateProjectionData(const SDimensions3D& dims);
bool zeroVolumeData(cudaPitchedPtr& D_data, const SDimensions3D& dims);
bool zeroProjectionData(cudaPitchedPtr& D_data, const SDimensions3D& dims);
bool copyVolumeToDevice(const float* data, cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch = 0);
bool copyProjectionsToDevice(const float* data, cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch = 0);
bool copyVolumeFromDevice(float* data, const cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch = 0);
bool copyProjectionsFromDevice(float* data, const cudaPitchedPtr& D_data, const SDimensions3D& dims, unsigned int pitch = 0);
bool duplicateVolumeData(cudaPitchedPtr& D_dest, const cudaPitchedPtr& D_src, const SDimensions3D& dims);
bool duplicateProjectionData(cudaPitchedPtr& D_dest, const cudaPitchedPtr& D_src, const SDimensions3D& dims);
bool transferVolumeToArray_1D(cudaPitchedPtr D_volumeData, cudaArray* array, const SDimensions3D& dims);
bool transferProjectionsToArray(cudaPitchedPtr D_projData, cudaArray* array, const SDimensions3D& dims);
bool transferVolumeToArray(cudaPitchedPtr D_volumeData, cudaArray* array, const SDimensions3D& dims);
bool zeroProjectionArray(cudaArray* array, const SDimensions3D& dims);
bool zeroVolumeArray(cudaArray* array, const SDimensions3D& dims);
cudaArray* allocateProjectionArray(const SDimensions3D& dims);
cudaArray* allocateVolumeArray(const SDimensions3D& dims);
cudaArray* transferDeformationToArray(const mxGPUArray * m_img);
bool bindDataTexture(const cudaArray* array, texture3D & Texture, cudaTextureAddressMode bordermode, bool normalized);
//float dotProduct3D(cudaPitchedPtr data, unsigned int x, unsigned int y, unsigned int z);
int calcNextPowerOfTwo(int _iValue);
bool cudaTextForceKernelsCompletion();
void reportCudaError(cudaError_t err);
double toc(double tstart);
double tic();
int checkLastError(char * msg);
void printFreeMemory();
int dumpArray(char* filename, int width, int height, float *buffer);
int dumpCudaArray(cudaPitchedPtr projData, int syart, int end, char * filename);
int writeImage(char * fname, int w, int h, float * data);
int writeBMPImage(char * fname, int w, int h, float ** red, float ** green, float ** blue);
int writeImageCudaArray(cudaPitchedPtr Data, int start, int end, char * filename);
}
#endif