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
+48
View File
@@ -0,0 +1,48 @@
%ASTRA_GPU_WRAPPER wrapper around the astra toolkit, it automatically
%recompiles the wrapper if some problems with the MEX file are detected
%
% varargout = ASTRA_GPU_wrapper(direction, input_array, cfg, vectors,output_array,varargin)
%
%
% ** direction 'fp' or 'bp' - forward or backward projection operator
% ** input_array either projected volume or backprojected projection array
% ** cfg cfg structed created by ASTRA_initialize
% ** vectors projection geometry created by ASTRA_initialize
%
% optional:
% ** output_array either reconstructed volume or projected array
% ** deformation_fields 3x2 or 3x1 cell array if deformation vector fields for nonrigid deformation tomography
%
%
% returns:
% ++ output resulting reconstruction, if output_array ~= [], result will
% be written to output_array directly to avoid memory allocation
function varargout = ASTRA_GPU_wrapper(direction, input_array, cfg, vectors,varargin)
varargout = cell(nargout,1);
assert(ismember(direction, {'fp', 'bp'}), 'Wrong option')
try
% call mex function
[varargout{:}] = ASTRA_GPU_wrapper(direction, input_array, cfg, vectors,varargin{:});
catch err
warning(err.identifier, 'ASTRA wrapper returned the following error: %s', err.message)
if any(strcmp(err.identifier, { 'MATLAB:UndefinedFunction','MATLAB:mex:ErrInvalidMEXFile'}))
path = replace(mfilename('fullpath'), mfilename, '');
utils.verbose(0, 'Trying to recompile the MEX function ... ')
mexcuda('-outdir',fullfile(path, 'private'), ...
fullfile(path, 'ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu'), ...
fullfile(path, 'ASTRA_GPU_wrapper/util3d.cu'), ...
fullfile(path, 'ASTRA_GPU_wrapper/par3d_fp.cu'), ...
fullfile(path, 'ASTRA_GPU_wrapper/par3d_bp.cu'));
[varargout{:}] = ASTRA_GPU_wrapper(direction, input_array, cfg, vectors,varargin{:});
else
utils.report_GPU_usage
rethrow(err)
end
end
end
@@ -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
+103
View File
@@ -0,0 +1,103 @@
% ASTRA_FIND_OPTIMAL_SPLIT Find optimal split of data and make the blocks sufficiently small for
% limited GPU memory
%
% split = ASTRA_find_optimal_split(cfg, num_gpu, angle_blocks, propagator)
%
% Inputs:
% **cfg - config structure generated by ASTRA_initialize
% **num_gpu - number of gpu to split the data
% **angle_blocks - number of angular blocks (ie in SART method or FSC)
% **propagator - which propagator should be assumed : FWD, BACK, both (default)
% Outputs:
% ++split - volume / angle split - [split_x,split_y,split_z,split_angles]
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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.
function split = ASTRA_find_optimal_split(cfg, num_gpu, angle_blocks, propagator)
if gpuDeviceCount == 0
split = 1;
return
end
gpu = gpuDevice;
if nargin < 2 || num_gpu == 0
num_gpu = 1;
end
if nargin < 3
angle_blocks = 1;
end
if nargin < 4
propagator = 'both';
end
Nangles = cfg.iProjAngles / angle_blocks;
if isfield(cfg, 'Grouping')
Nangles = min(Nangles, cfg.Grouping);
end
split = [1,1,1];
if ismember(lower(propagator), {'fwd','both'})
split = max(split, ceil([cfg.iVolX, cfg.iVolY, cfg.iVolZ] / 4096 )); % texture memory limit
split = max(split, [1,1,ceil( (cfg.iVolX*cfg.iVolY*cfg.iVolZ*4) / 1.024e9 / prod(split)/num_gpu)]); % texture memory limit
end
split = max(split, [1,1,split(3)*ceil( ((cfg.iVolX*cfg.iVolY*cfg.iVolZ*4)/ prod(split)/num_gpu) / (gpu.AvailableMemory/2 - min(1.1e9, cfg.iVolX*cfg.iVolY*cfg.iVolZ*4)) )]); % gpu memory limit
split = max(split, [1,1,split(3)*ceil( ((cfg.iVolX*cfg.iVolY*cfg.iVolZ )/ prod(split)/num_gpu) / double(intmax('int32')) )]); % maximal array on GPU limit
if ismember(lower(propagator), {'back','both'})
% if projection would be larger than 4096x4096 -> split the reconstruction volume
split = max(split, [cfg.iProjU, cfg.iProjU, cfg.iProjV]/4096 ); % texture memory limit
end
% projection size limitation + astra allows only < 1024 angles
split(4) = max(ceil(Nangles/1024), ceil( (cfg.iProjU*cfg.iProjV*min(1024,Nangles)*4) / gpu.TotalMemory/num_gpu)); % gpu memory limit
% RAM limits
if cfg.iVolX*cfg.iVolY*cfg.iVolZ > 2e6
freemem = 0.8 * utils.check_available_memory;
split(4) = max(split(4), num_gpu*(4*(cfg.iProjU*cfg.iProjV*cfg.iProjAngles...
+ cfg.iVolX*cfg.iVolY*cfg.iVolZ*(1+1/prod(split(1:3)))))...
/ (freemem * 1e6) ); % RAM memory limit
end
split(4) = ceil(split(4));
split(1:3) = 2.^nextpow2(split(1:3));
% if any(split ~= 1)
% fprintf('Automatically splitting to %ix%ix%ix(%i) cubes \n', split);
% end
end
+299
View File
@@ -0,0 +1,299 @@
% ASTRA_INITIALIZE Generate inputs needed for astra MEX wrapper
%
% [cfg, vectors] = ASTRA_initialize(Npix, size_projection,angles,lamino_angle, tilt_angle, pixel_scale, rotation_center)
%
% Inputs for angular geometry (!! all angles are expected in degress !!):
% **Npix - size of tomogram
% **size_projection - size of sinogram (Nlayers, width, Nangles)
% **angles - rotation angles of projections in degrees
% *optional*:
% **lamino_angle - laminography angle / angles in degrees. lamino_angle ==
% 90 is standard tomography , default = 90
% **tilt_angle - tilt of camera with respect to the rotation axis coordinates, in degrees, default = 0
% **pixel_scale - scale of pixels in tomogram compares to the
% projection pixel size, default = 1
% **rotation_center - center of rotation coordinates, default = size_projection/2
% **skewness_angle - distorsion of parallel axis by [1, sind(alpha); 0, 1]
%
% Inputs for rotation matrix geometry:
% **Npix - size of tomogram
% **size_projection - size of sinogram (Nlayers, width, Nangles)
% **rotation_matrix - R is a 3x3xn matrix, for n projections
%
% Outputs:
% ++cfg - config structure for ASTRA mex wrapper
% ++vectors - parameter vector for each angle
%*-----------------------------------------------------------------------*
%| |
%| 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.
function [cfg, vectors] = ASTRA_initialize(Npix, size_projection, varargin)
use_rotmat = length(varargin) == 1 && size(varargin{1},1) == 3 && size(varargin{1},2) == 3 ;
if use_rotmat
% rotation matrices were provided
rot_mat = varargin{1};
Nangles = size(rot_mat,3);
r.rotation_center = size_projection/2; % only centered geometry is supported when rotation matrix is provided
else
% angles and other parameters were provided
par = inputParser;
par.KeepUnmatched = true;
%% ALL angles are assumed in degrees
par.addRequired('angles') % rotation angles of projections in degrees
par.addOptional('lamino_angle', 90, @isnumeric) % laminography angle / angles in degrees. lamino_angle == 90 is standard tomography , default = 90
par.addOptional('tilt_angle', 0, @isnumeric) % tilt of camera with respect to the rotation axis coordinates, in
par.addOptional('pixel_scale', [1,1], @isnumeric) % scale of pixels in tomogram compares to the projection pixel size, default = 1
par.addOptional('rotation_center', size_projection/2, @isnumeric) % center of rotation cooridinates, default = size_projection/2
par.addOptional('skewness_angle', 0) % distorsion of parallel axis by [1, sind(alpha); 0, 1]
par.addParameter('show_geometry', false) % plot also a geometry for each projection
par.parse(varargin{:})
r = par.Results;
Nangles = length(r.angles);
end
% angles should be sorted in order to maximize performance of the astra
% toolbox -> better use of texture memory
assert(math.isint(Npix), 'Npix is not integer');
assert(math.isint(size_projection), 'size_projection is not integer');
if isscalar(Npix)
Npix(2) = Npix;
end
if length(Npix) == 2 && all(r.lamino_angle == 90)
Npix(3) = size_projection(1); % default behaviour is to have same number of layers in reconstruction and in laminography
elseif length(Npix) == 2 && any(r.lamino_angle ~= 90)
error('All three dimensions of the volume size has to be specified for the laminograhy geometry')
end
cfg.iVolX = Npix(1);
cfg.iVolY = Npix(2);
cfg.iVolZ = Npix(3);
cfg.iProjAngles = Nangles;
cfg.iProjU = size_projection(2);
cfg.iProjV = size_projection(1);
cfg.iRaysPerDet = 1;
cfg.iRaysPerDetDim = 1;
cfg.iRaysPerVoxelDim = 1;
source_distance = 1; % currenlty not implemented in the ASTRA wrapper
if use_rotmat
[vectors] = astra_convert_R_vectors(rot_mat,Nangles);
else
% compatibility with iradonfast
r.angles = r.angles + 90;
cfg.lamino_angle = r.lamino_angle;
cfg.pixel_scale = r.pixel_scale;
cfg.tilt_angle = r.tilt_angle;
cfg.skewness_angle = r.skewness_angle;
vectors = ASTRA_get_geometry(r.angles, r.lamino_angle, r.tilt_angle, source_distance,r.pixel_scale,r.skewness_angle,r.show_geometry);
end
%%%% apply geometry correction to shift reconstruction into center %%%%%%%%%%%%%%%%%%%%%%%%%%
vectors(:,4:6) = vectors(:,4:6) -(vectors(:,10:12).*(r.rotation_center(:,1) )+vectors(:,7:9).*(r.rotation_center(:,2) ));
end
function vectors = ASTRA_get_geometry(angles, lamino_angle, tilt_angle, source_distance,pixel_scale,skewness_angle,show)
Nangles = numel(angles);
% angles should be sorted in order to maximize performance of the astra
% toolbox -> better use of texture memory
angles = deg2rad(angles(:));
lamino_angle = pi/2 - deg2rad(lamino_angle);
tilt_angle = deg2rad(tilt_angle);
skewness_angle = deg2rad(skewness_angle);
if isscalar(lamino_angle)
lamino_angle = lamino_angle .* ones(Nangles,1);
end
if isscalar(tilt_angle)
tilt_angle = tilt_angle .* ones(Nangles,1);
end
if isscalar(skewness_angle)
skewness_angle = skewness_angle .* ones(Nangles,1);
end
if isscalar(pixel_scale) || numel(pixel_scale) == 2
pixel_scale = bsxfun(@times, pixel_scale , ones(Nangles,2));
end
% We generate the same geometry as the circular one above.
vectors = zeros(Nangles, 12);
% ray direction
vectors(:,1) = sin(angles).*cos(lamino_angle);
vectors(:,2) = -cos(angles).*cos(lamino_angle);
vectors(:,3) = sin(lamino_angle);
vectors(:,1:3) = vectors(:,1:3) .*source_distance;
% center of detector
vectors(:,4:6) = 0;
% vector from detector pixel (0,0) to (0,1)
vectors(:,7) = cos(angles)./pixel_scale(:,1);
vectors(:,8) = sin(angles)./pixel_scale(:,1);
vectors(:,9) = 0/pixel_scale(:,1);
% vector from detector pixel (0,0) to (1,0)
% cross(vectors(i,1:3), vectors(i,7:9))
% dot(vectors(i,1:3), vectors(i,7:9))
vectors(:,10) = - sin(lamino_angle).*sin(angles)./pixel_scale(:,2);
vectors(:,11) = sin(lamino_angle).*cos(angles)./pixel_scale(:,2);
vectors(:,12) = cos(lamino_angle)./pixel_scale(:,2);
% Rodrigues' rotation formula - rotate detector in plane
% perpendicular to the beam axis
if any(tilt_angle ~= 0)
for i = 1:Nangles
vectors(i,7:9)=vectors(i,7:9).*cos(tilt_angle(i)) + ...
cross(vectors(i,1:3), vectors(i,7:9)).*sin(tilt_angle(i)) + ...
(vectors(i,1:3)*dot(vectors(i,1:3),vectors(i,7:9))).*(1-cos(tilt_angle(i)));
vectors(i,10:12)=vectors(i,10:12).*cos(tilt_angle(i)) + ...
cross(vectors(i,1:3), vectors(i,10:12)).*sin(tilt_angle(i)) + ...
(vectors(i,1:3).*dot(vectors(i,1:3),vectors(i,10:12))).*(1-cos(tilt_angle(i)));
end
end
% search also for skewness => the same as rotation, but rotate
% only one axis of the detector !!
if any(skewness_angle ~= 0)
for i = 1:Nangles
vectors(i,10:12)=vectors(i,10:12).*cos(skewness_angle(i)/2) + ...
cross(vectors(i,1:3), vectors(i,10:12)).*sin(skewness_angle(i)/2) + ...
(vectors(i,1:3).*dot(vectors(i,1:3),vectors(i,10:12))).*(1-cos(skewness_angle(i)/2));
end
end
%% PLOT THE CURRENT SETUP
if show
for i = 1:Nangles
draw_projection_geometry(vectors(i,:))
end
end
end
function [vectors] = astra_convert_R_vectors(rot_mat,Nangles)
% R is defined as in the arbitrary projection code by Manuel
% This integrates along z (2nd index) and so this code follows this
% convention.
pixel_scale(1:Nangles,1:2) = [1];
convert_matrix = [0 0 1
0 1 0
1 0 0];
for ii=1:size(rot_mat,3)
rot_mat(:,:,ii)=rot_mat(:,:,ii)*convert_matrix;
end
vectors = zeros(Nangles, 12);
for i = 1:Nangles
% before starting to mess around: works with a correction matrix
% with the magnetic contrast, but not for the laminography
vectors(i,1) = -rot_mat(3,1,i);
vectors(i,2) = -rot_mat(3,3,i);
vectors(i,3) = -rot_mat(3,2,i);
vectors(i,4:6) = 0;
vectors(i,7) = rot_mat(1,1,i)/pixel_scale(i,1);
vectors(i,8) = rot_mat(1,3,i)/pixel_scale(i,1);
vectors(i,9) = rot_mat(1,2,i)/pixel_scale(i,1);
vectors(i,10) = rot_mat(2,1,i)/pixel_scale(i,2);
vectors(i,11) = rot_mat(2,3,i)/pixel_scale(i,2);
vectors(i,12) = rot_mat(2,2,i)/pixel_scale(i,2);
end
end
function draw_projection_geometry(vectors)
% show geometry saved in the "vectors" matrix
ray = vectors(1:3);
c_center = vectors(4:6)-ray;
c_origin = c_center - vectors( 7:9)/2-vectors( 10:12)/2;
k = 6;
n = 2^k-1;
[x,y,z] = sphere(n);
c = hadamard(2^k);
s = 0.5;
figure(15)
surf(vectors(4)+s*x,vectors(5)+s*y,vectors(6)+s*z,c);
shading flat
colormap([1 1 0; 0 1 1])
hold all
plot3d_vec(c_origin, vectors( 7:9), 'r');
plot3d_vec(c_origin, vectors( 10:12), 'r');
plot3d_vec(c_origin+vectors( 7:9), vectors( 10:12), 'r');
plot3d_vec(c_origin+vectors( 10:12), vectors( 7:9), 'r');
% draw "pixels" on a 10x10 grid
for x = linspace(0,1,10)
plot3d_vec(c_origin+x*vectors( 10:12), vectors( 7:9), 'r:');
plot3d_vec(c_origin+x*vectors( 7:9),vectors( 10:12), 'r:');
end
plotting.mArrow3(c_center+ray*2,c_center, 'color', 'blue', 'stemWidth',0.02,'facealpha',0.5);
hold off
axis([-1,1,-1,1,-1,1])
drawnow
end
function h_out = plot3d_vec(x0, vec, varargin)
h = plot3(x0(1)+[0,vec(1)], ...
x0(2)+[0,vec(2)], ...
x0(3)+[0,vec(3)], varargin{:});
if nargout > 1
h_out = h;
end
end
+339
View File
@@ -0,0 +1,339 @@
% AX_PARTIAL backprojector that allows to split the full volume into smaller pieces
% composed tomography back-projector based on ASTRA toolbox
% can be used either for data in RAM or on GPU (automatically decided from class of volData)
% * volume is split based on "split" parameter, 1 == no splitting
% * Ax_partial tries to split data if GPU limits are exceeded (ie texture memory limits)
%
% vol_full = Atx_partial(projData, cfg, vectors,split,varargin)
%
% Inputs:
% **projData - array Nx x Ny x Nz of projected volume
% **cfg - config structure generated by ASTRA_initialize
% **vectors - orientation of projections generated by ASTRA_initialize
% **split - 3 or 4 elements vector, [split X, split Y, split Z, split angle ]
% *optional*
% **deformation_fields - 3x1 cell contaning 3D arrays of local deformation of the object
% **GPU - GPU id to be used for reconstruction
% **verbose - verbose = 0 : (default) quiet, verbose = 1: standard info , verbose = 2: debug
% **keep_on_GPU - if true keep reconstructed volume on GPU to make is faster, default == false (the safe option)
%
% *returns*
% ++vol_full - projection of volData
%
% 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
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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.
function vol_full = Atx_partial(projData, cfg, vectors,split,varargin)
import utils.*
import math.*
par = inputParser;
par.KeepUnmatched = true;
par.addOptional('deformation_fields', {}) % deformation_fields: 3x1 cell contaning 3D arrays of local deformation of the object
par.addOptional('GPU', []) % GPUs id to be used in reconstruction
par.addOptional('keep_on_GPU', false) % true - keep reconstructed volume on GPU to make is faster
par.addOptional('verbose', 0) %
par.parse(varargin{:})
r = par.Results;
if isempty(r.deformation_fields); r.deformation_fields = {}; end
%% check the inputs + check memory availibility on GPU
assert(gpuDeviceCount>0, 'No CUDA enabled GPU availible')
if ~( (isa(projData, 'gpuArray') && strcmp(classUnderlying(projData), 'single')) || ...
isa(projData, 'single') ) || ~isreal(projData)
error('Only single precision real input array supported')
end
if ~isempty(r.deformation_fields)
assert(any(numel(r.deformation_fields) == [3,6]), 'Deformation field expected as 3x1 or 6x1 cell array')
for i = 1:numel(r.deformation_fields)
if ~( (isa(r.deformation_fields{i}, 'gpuArray') && strcmp(classUnderlying(r.deformation_fields{i}), 'single')) || ...
isa(r.deformation_fields{i}, 'single'))
error('Only single precision for deformation fields is supported')
end
r.deformation_fields{i} = gpuArray(r.deformation_fields{i}); % move on GPU, they are usually small
end
r.deformation_fields = r.deformation_fields' ; % transpose to that array(:) results in sorted field
end
projSize = size(projData);
cfg.iProjAngles = size(vectors,1); % ignore value in config
gpu = gpuDevice();
if ~isempty(r.GPU) && gpu.Index ~= r.GPU(1)
% switch and !! reset !! GPU
if isa(projData, 'gpuArray'), error('Switching GPUs will reset content'); end
gpu = gpuDevice(r.GPU(1));
end
assert(cfg.iVolX*cfg.iVolY*cfg.iVolZ > 0, 'Volume is empty');
assert(numel(projSize) > 0, 'Projections are empty');
% input parameters check
if ismatrix(projData); projSize = [projSize,1]; end
assert(max(cfg.iProjU, cfg.iProjV) <= 4096, 'Sinogram exceed maximal size allowed by GPU (4096)')
assert(all(projSize==[cfg.iProjV,cfg.iProjU,cfg.iProjAngles]), 'Wrong inputs size')
assert(all(size(vectors)==[cfg.iProjAngles,12]), 'Wrong vectors size')
split_projections = cfg.iProjAngles > 1024 || ...
numel(projData)*4 > 1024e6 || ... ; % Data array is to large, try splitting
(length(split) > 3 && split(4) > 1); % split contains also angular split
% be sure that ASTRA wrapper is feeded by doubles !!
for i = fieldnames(cfg)'
cfg.(i{1}) = double(cfg.(i{1}));
end
vectors = double(vectors);
split=double(split);
if all(split == 1) && ~split_projections
assert(cfg.iVolX*cfg.iVolY*cfg.iVolZ*4 < min(4*double(intmax('int32')),gpu.AvailableMemory), 'Volume array is too large for GPU, use Atx_sup_partial');
%% in the simplest case call ASTRA_GPU_wrapper directly
if isa(projData, 'gpuArray'), r.keep_on_GPU = true; end % assume that the data should stay on GPU if provided
projData = matlab2astra(gpuArray(projData));
vol_full = astra.ASTRA_GPU_wrapper('bp',projData, cfg, vectors,[],r.deformation_fields{:});
if ~r.keep_on_GPU; vol_full = gather(vol_full); end
return
end
%% otherwise prepare data for split and call ASTRA_GPU_wrapper on subvolumes
split = ceil(max(1,split));
if isscalar(split)
split = split .* ones(3,1);
end
if length(split) < 4
split(4) = 1;
end
%% PREPARE VOLUME %%
Npix_full = [cfg.iVolX,cfg.iVolY,cfg.iVolZ];
Npix_small = Npix_full(:)./reshape(split(1:3),[],1);
cfg.iVolX = Npix_small(1);
cfg.iVolY = Npix_small(2);
cfg.iVolZ = Npix_small(3);
assert( all(mod(Npix_small,1)==0), sprintf('Volume array cannot be divided to %i %i %i cubes', split))
assert(prod(Npix_small)*4 < min(4*double(intmax('int32')),gpu.AvailableMemory), 'Volume array is too large for GPU, use Atx_sup_partial');
req_vol_memory = 4*(cfg.iVolX*cfg.iVolY*cfg.iVolZ) * (1+any(split(1:3)>1));
keep_volume_on_GPU = isa(projData, 'gpuArray') ||...
(gpu.AvailableMemory * 0.5 > req_vol_memory) && ...
( (cfg.iVolX*cfg.iVolY*cfg.iVolZ) < intmax('int32') );
% preallocate large array for results
if keep_volume_on_GPU || prod(split(1:3)) == 1
% transfer to GPU now
vol_full = gpuArray.zeros(Npix_full, 'single');
else
% keep the volume in RAM
vol_full = zeros(Npix_full, 'single');
end
%% PREPARE PROJECTIONS %%
% fix if the number of projections is > 1024, or arrays are too large
Nproj_groups = max(split(4),ceil(max([cfg.iProjAngles/1024, ...
cfg.iProjU*cfg.iProjV*cfg.iProjAngles*4 / min(1024e6,gpu.AvailableMemory-1.024e9), ...
cfg.iProjU * cfg.iProjV * cfg.iProjAngles / double(intmax('int32'))])));
% avoid some rounding issues during splitting
Nproj_groups = ceil(cfg.iProjAngles / floor(cfg.iProjAngles/Nproj_groups));
if r.verbose > 1
fprintf('Size of the full volume: %i %i %i\n', Npix_full)
fprintf('Size of the subvolume: %i %i %i\n', Npix_small)
fprintf('Size of the data: %i %i %i\n', size(projData))
fprintf('Free GPU memory: %3.2g%%\n', gpu.AvailableMemory/gpu.TotalMemory*100)
end
if Nproj_groups > 1
% fix if the number of projections is > 1024 (limitation of the ASTRA code )
% also if the dataset is too large (>1024MB), do automatic splitting along angles
for i = 1:Nproj_groups
ind = (1+(i-1)*ceil(cfg.iProjAngles/Nproj_groups)):i*ceil(cfg.iProjAngles/Nproj_groups);
ind = ind(ind <= cfg.iProjAngles);
proj_ind{i} = ind;
vectors_tmp{i} = vectors(ind,:);
cfg_tmp{i} = cfg;
cfg_tmp{i}.iProjAngles = length(ind);
end
cfg = cfg_tmp; vectors = vectors_tmp;
clear projData_tmp
else
Nproj_groups = 1;
cfg = {cfg};
vectors = {vectors};
end
if ~isempty(r.deformation_fields) && any(split(1:3) > 1)
error('Deformation field splitting not implemented')
end
if gpu.AvailableMemory > 4*(numel(projData)+ ...
cfg{1}.iProjU* cfg{1}.iProjV*cfg{1}.iProjAngles * (Nproj_groups>1) ...
+any(split>1)*prod(Npix_small)*(2 + ~isa(vol_full, 'gpuArray'))) && ...
numel(projData) < intmax('int32')
projData = gpuArray(projData); % move small blocks directly on GPU
end
inParpool = ~isempty(getCurrentTask());
iter = 1;
for k = 1:Nproj_groups
if Nproj_groups > 1
if length(proj_ind{k}) > 1 && ~inParpool && ~isa(projData, 'gpuArray') && exist('+tomo/get_from_array.m', 'file')
% use custom made MEX function from +tomo package, usually
% faster but use a lot of CPU
projData_small = tomo.get_from_array(projData, [], proj_ind{k}); % load subblock from projections
else
projData_small = projData(:,:,proj_ind{k}); % split the projections if needed
end
else
projData_small = projData;
end
if gpu.AvailableMemory < 2*4*numel(projData_small)
% in case of low GPU memory, transpose data in RAM
projData_small = gpuArray(matlab2astra(projData_small)); % transfer projections to GPU if not there yet
else
% move subblocks of the data on GPU
projData_small = gpuArray(projData_small);
projData_small = matlab2astra(projData_small);
end
if numel(projData_small) * 4 > 1024e6
error('Data exceeded maximal size of texture memory 1024MB, Increase "split" to reduce the projection size')
end
if gpu.AvailableMemory < prod(Npix_small)*4 && prod(split(1:3)) ~= 1
% memory needed to make CUDA array for texture memory
pause(0.1)
!nvidia-smi
whos
error('Too low GPU memory, avail: %3.2gGB / req: %3.2gGB, GPU %i/%i', gpu.AvailableMemory/1e9,prod(Npix_small)*4/1e9, gpu.Index, gpuDeviceCount)
end
for z = 1:split(3)
for x = 1:split(1)
for y =1:split(2)
if r.verbose > 0
progressbar(iter, prod(split(1:3))*Nproj_groups+1, 20);
end
pos = [x,y,z];
for n = 1:3
% find optimal shift of the subvolume
if mod(split(n),2)==1 %% odd
shift(n) = (pos(n) - ceil(split(n)/2))*Npix_small(n);
else
shift(n) = (pos(n) - ceil(split(n)/2)-1/2)*Npix_small(n);
end
end
vectors_tmp = vectors{k};
vectors_tmp(:,4:6) = bsxfun(@minus, vectors_tmp(:,4:6), shift);
if prod(split(1:3))== 1
req_mem = numel(projData_small)*4 ;
else
req_mem = 2*numel(projData_small)*4 ;
end
if gpu.AvailableMemory < req_mem
error('Too low GPU memory, avail: %3.2gGB / req: %3.2gGB, GPU %i/%i', gpu.AvailableMemory/1e9,req_mem/1e9, gpu.Index, gpuDeviceCount)
end
try
if prod(split(1:3))== 1
% no splitting, vol_small == vol_full -> write
% the backprojection directly to vol_full
% without copying -> no output arguments are needed
astra.ASTRA_GPU_wrapper('bp', projData_small, cfg{k}, vectors_tmp,vol_full,r.deformation_fields{:});
else
vol_small = astra.ASTRA_GPU_wrapper('bp', projData_small, cfg{k}, vectors_tmp,[],r.deformation_fields{:});
% if some volume split is needed, add the subvolume
% to the full volume
if ~keep_volume_on_GPU
vol_small = gather(vol_small); % return to RAM
end
if keep_volume_on_GPU || isa(vol_full, 'gpuArray') || ~exist('+tomo/add_to_3D_volume.m', 'file')
% return results to the large array
% use matlab to do the copying on GPU
vol_full =add_to_3D(vol_full, vol_small,([x,y,z]-1).*Npix_small');
else
% otherwise use paralelized CPU code
tomo.add_to_3D_volume(vol_full,vol_small, ([x,y,z]-1).*Npix_small', true);
end
end
catch err
if strcmpi(err.identifier,'parallel:gpu:array:OOM')
warning('Out of memory on GPU %i, try reset GPU or split the array onto smaller blocks', gpu.Index)
gpuDevice
end
reset(gpuDevice)
rethrow(err)
end
iter = iter+1;
if r.verbose > 0
progressbar(iter, prod(split(1:3))*Nproj_groups+1, 20);
end
end
end
end
end
if ~r.keep_on_GPU
vol_full = gather(vol_full);
end
end
+385
View File
@@ -0,0 +1,385 @@
% AX_PARTIAL forward projector that allows to split the full volume into smaller pieces
% composed tomography projector based on ASTRA toolbox
% can be used either for data in RAM or on GPU (automatically decided from class of volData)
% * volume is split based on "split" parameter, 1 == no splitting
% * Ax_partial tries to split data if GPU limits are exceeded (ie texture memory limits)
%
% projData = Ax_partial(volData, cfg, vectors,split, varargin)
%
% Inputs:
% **volData - array Nx x Ny x Nz of projected volume
% **cfg - config structure generated by ASTRA_initialize
% **vectors - orientation of projections generated by ASTRA_initialize
% **split - 3 or 4 elements vector, [split X, split Y, split Z, split angle ]
% *optional*
% **deformation_fields - 3x1 cell contaning 3D arrays of local deformation of the object
% **GPU - GPU id to be used for reconstruction
% **verbose - verbose = 0 : (default) quiet, verbose = 1: standard info , verbose = 2: debug
% **keep_on_GPU - if true keep reconstructed volume on GPU to make is faster, default == false (the safe option)
%
% *returns*
% ++projData - projection of volData
%
% 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
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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.
function projData = Ax_partial(volData, cfg, vectors,split, varargin)
import utils.*
import math.*
par = inputParser;
par.KeepUnmatched = true;
par.addOptional('deformation_fields', {}) % deformation_fields: 3x1 cell contaning 3D arrays of local deformation of the object
par.addOptional('GPU', []) % GPUs id to be used in reconstruction
par.addOptional('verbose', 0) % verbose = 0 : quiet, verbose : standard info , verbose = 2: debug
par.addOptional('keep_on_GPU', false) % true - keep reconstructed volume on GPU to make is faster
par.parse(varargin{:})
r = par.Results;
if isempty(r.deformation_fields); r.deformation_fields = {}; end
%% check the inputs + check memory availibility on GPU
assert(gpuDeviceCount>0, 'No CUDA enabled GPU availible')
if ~( (isa(volData, 'gpuArray') && strcmp(classUnderlying(volData), 'single')) || ...
isa(volData, 'single') ) || ~isreal(volData)
error('Only single precision real input array supported')
end
if ~isempty(r.deformation_fields)
assert(any(numel(r.deformation_fields) == [3,6]), 'Deformation field expected as 3x1 or 6x1 cell array')
for i = 1:numel( r.deformation_fields)
if ~( (isa(r.deformation_fields{i}, 'gpuArray') && strcmp(classUnderlying(r.deformation_fields{i}), 'single')) || ...
isa(r.deformation_fields{i}, 'single'))
error('Only single precision for deformation fields is supported')
end
r.deformation_fields{i} = gpuArray(r.deformation_fields{i}); % move on GPU, they are usually small
end
r.deformation_fields = r.deformation_fields' ; % transpose to that array(:) results in sorted field
end
gpu = gpuDevice();
if ~isempty(r.GPU) && gpu.Index ~= r.GPU(1)
% switch and !! reset !! GPU
if isa(volData, 'gpuArray'), error('Switching GPUs will reset content'); end
gpu = gpuDevice(r.GPU(1));
end
split = ceil(max(1,split));
if ismatrix(volData)
split = [split([1, min(2,end)]),1];
assert(all(size(volData)==[cfg.iVolX,cfg.iVolY]), 'Wrong inputs size')
else
assert(all(size(volData)==[cfg.iVolX,cfg.iVolY,cfg.iVolZ]), 'Wrong inputs size')
end
keep_on_GPU = isa(volData, 'gpuArray') || r.keep_on_GPU;
%% backprojector that allows to split the full volume into smaller pieces
assert(all(size(vectors,2)==12), 'Wrong "vectors" size')
assert(~isempty(vectors), 'Empty input "vectors"')
assert(cfg.iVolX*cfg.iVolY*cfg.iVolZ > 0, 'Inputs volume is empty');
assert(cfg.iProjU*cfg.iProjV*cfg.iProjAngles > 0, 'Projections are empty');
% be sure that ASTRA wrapper is feeded by doubles !!
for i = fieldnames(cfg)'
cfg.(i{1}) = double(cfg.(i{1}));
end
vectors = double(vectors);
split=double(split);
cfg.iProjAngles = size(vectors,1);
assert(cfg.iProjAngles > 1, 'Number of processed angles must be > 1')
% fix if the number of projections is > 1024, or arrays are too large
Nproj_groups = max(1,ceil(max([cfg.iProjAngles/1024, ... % ASTRA constant memory limit
cfg.iProjU*cfg.iProjV*cfg.iProjAngles*4 / gpu.AvailableMemory, ... % availible memory limit
cfg.iProjU * cfg.iProjV * cfg.iProjAngles / double(intmax('int32'))]))); % maximal array size allowed by CUDA limit
if length(split) > 3
Nproj_groups = max(split(4), Nproj_groups);
end
if all(split == 1) && Nproj_groups == 1
if numel(volData)*4 > 1024e6 % exceeded texture memory
nsubVol = ceil(numel(volData)*4 / 1024e6);
nsubVol = 2^nextpow2(nsubVol);
% split = ceil([sqrt(nsubVol),sqrt(nsubVol),1]);
split = [1,1,nsubVol];
if r.verbose>0; disp(['Volume array is larger than 1024MB, auto-splitting ', num2str(split)]); end
else
%% in the simple case call ASTRA_GPU_wrapper directly
volData = gpuArray(volData);
% call ASTRA
projData = astra.ASTRA_GPU_wrapper('fp',volData, cfg, vectors,[],r.deformation_fields{:});
clear volData
if gpu.AvailableMemory < 4*numel(projData)
projData = gather(projData); % prevent out of memory errors during next step
end
projData = astra2matlab(projData);
if ~keep_on_GPU; projData = gather(projData); end
return
end
end
%% otherwise prepare data for split and call ASTRA_GPU_wrapper on subvolumes
if isscalar(split)
split = split .* ones(ndims(volData),1);
end
assert(numel(volData)*4/prod(split) <= 1024e6, 'Volume array exceeded 1024MB, use more splitting')
Nvol_full = size(volData);
if numel(Nvol_full)<3
Nvol_full(3)=1;
end
Nvol_sub = Nvol_full'./reshape(split(1:3),[],1);
assert(all(mod(Nvol_sub,1)==0), sprintf('Volume size %ix%ix%i is not dividable by split %ix%ix%i',size(volData),split(1:3)))
if ismatrix(volData)
split(3) = 1;
Nvol_sub(3) = 1;
end
cfg.iVolX = cfg.iVolX/split(1);
cfg.iVolY = cfg.iVolY/split(2);
cfg.iVolZ = cfg.iVolZ/split(3);
cfg_orig = cfg;
if Nproj_groups > 1
% split the projections along the angles
for i = 1:Nproj_groups
ind = (1+(i-1)*ceil(cfg.iProjAngles/Nproj_groups)):i*ceil(cfg.iProjAngles/Nproj_groups);
ind = ind(ind <= cfg.iProjAngles);
vectors_tmp{i} = vectors(ind,:);
cfg_tmp{i} = cfg;
cfg_tmp{i}.iProjAngles = length(ind);
end
cfg = cfg_tmp; vectors = vectors_tmp;
else
Nproj_groups = 1;
cfg = {cfg};
vectors = {vectors};
end
clear ind
if r.verbose > 1
fprintf('Size of the full volume: %i %i %i\n', size(volData))
fprintf('Size of the subvolume: %i %i %i\n', Nvol_sub)
fprintf('Size of the one sinogram block: %i %i %i\n', cfg{1}.iProjU, cfg{1}.iProjV, cfg{1}.iProjAngles)
end
%%!!!! note that in rare cases astra my fail if sinogram width is too small
assert(prod(Nvol_sub) * 4 <= 1024e6, 'Volume exceeded maximal size of texture 1024MB')
% estimate required memory + (use only if 2x more memory is available)
required_mem = 2*(prod(Nvol_sub)*(2+~isa(volData, 'gpuArray')) + cfg_orig.iProjU * cfg_orig.iProjV * cfg_orig.iProjAngles )*4;
% keep projections on GPU only of there is enough memory
keep_projections_on_GPU = Nproj_groups == 1 || gpu.AvailableMemory > required_mem;
if cfg{1}.iProjU * cfg{1}.iProjV * cfg{1}.iProjAngles > intmax('int32')
error('Projection size exceeded maximum size allowed on GPU')
end
if (keep_on_GPU || prod(split(1:3)) == 1) && numel(volData) < intmax('int32') % keep volume on GPU
volData = gpuArray(volData);
end
inParpool = ~isempty(getCurrentTask());
iter = 1;
for m = 1:Nproj_groups
% split angularly (solve smaller groups of angles)
% allocate memory for the projections
projData{m} = gpuArray.zeros(cfg{m}.iProjU, cfg{m}.iProjV, cfg{m}.iProjAngles, 'single');
% split into volume cubes
for i = 1:split(1)
for j = 1:split(2)
for k = 1:split(3)
if r.verbose > 0
progressbar(iter, prod(split(1:3))*Nproj_groups+1, 20);
end
pos = [i,j,k];
for n = 1:3
ind{n} = (1+(pos(n)-1)*Nvol_sub(n)):(pos(n)*Nvol_sub(n));
%% find optimal shift of the subvolume
if mod(split(n),2)==1 %% odd
shift(n) = (pos(n) - ceil(split(n)/2))*Nvol_sub(n);
else
shift(n) = (pos(n) - ceil(split(n)/2)-1/2)*Nvol_sub(n);
end
end
% extract subvolume to be processed
if any(split(1:3)~=1) && isa(volData, 'gpuArray')
vol_small = volData(ind{:}); % take only small subvolume
elseif any(split(1:3)~=1)
vol_small = zeros(Nvol_sub','single');
utils.get_from_3D_projection(vol_small,volData,[ind{1}(1),ind{2}(1)]-1,ind{3});
else
vol_small = volData; % avoid data copying of possible
end
vol_small = gpuArray(vol_small);
% split deformation field for nonrigid tomography
if ~isempty(r.deformation_fields)
for ii = 1:3
N_deform = size(r.deformation_fields{ii}) ./ reshape(split(1:3),[],1)';
for jj = 1:3
ind_def{jj} = linspace(1+(pos(jj)-1)*N_deform(jj), pos(jj)*N_deform(jj), size(r.deformation_fields{ii},jj));
end
[X,Y,Z]= meshgrid(ind_def{:});
deformation_fields_sub{ii} = interp3(r.deformation_fields{ii},X,Y,Z);
end
else
deformation_fields_sub = {};
end
vec = vectors{m};
vec(:,4:6) = bsxfun(@minus, vec(:,4:6), shift);
req_mem = 2*numel(vol_small)*4 ;
if gpu.AvailableMemory < req_mem
!nvidia-smi
whos
error('Too low GPU memory, avail: %3.2gGB / req: %3.2gGB, GPU %i/%i, projection group %i/%i, keep_proj_on_GPU=%i', gpu.AvailableMemory/1e9,req_mem/1e9, gpu.Index, gpuDeviceCount, m , Nproj_groups, keep_projections_on_GPU)
end
try
% avoid memory allocation, write directly to projData{m} -> no output arguments are needed
astra.ASTRA_GPU_wrapper('fp',vol_small, cfg{m}, vec,projData{m}, deformation_fields_sub{:});
vol_small = []; % soft mem clean
catch err
if strcmpi(err.identifier,'parallel:gpu:array:OOM')
warning('Out of memory on GPU %i, try reset GPU or split the array onto smaller blocks', gpu.Index)
gpuDevice
reset(gpuDevice)
end
rethrow(err)
end
iter = iter+1;
if r.verbose>0
progressbar(iter, prod(split(1:3))*Nproj_groups+1, 20);
end
end
end
end
if ~keep_projections_on_GPU
projData{m} = gather(projData{m});
end
end
clear volData vol_small
% permute / concatenate
if gpu.AvailableMemory < 4*numel(projData{1})*max(2,Nproj_groups)
projData = gather_all(projData);
end
projData = astra2matlab(projData);
if gpu.AvailableMemory < 8*numel(projData{1})*Nproj_groups
projData = gather_all(projData);
end
% concatenate the projected data align the angular (3rd) axis
projData = merge_projections(projData);
if ~keep_on_GPU
projData = gather(projData);
elseif numel(projData) < intmax('int32') && gpu.AvailableMemory < 4*numel(projData)
% return to GPU if requested by 'keep_on_GPU' parameter
projData = gpuArray(projData);
end
end
function x = gather_all(x)
for i = 1:length(x)
x{i} = gather(x{i});
end
end
function projData = merge_projections(projData_blocks)
Nblocks = length(projData_blocks);
if Nblocks > 1
if isa(projData_blocks{1}, 'gpuArray')
projData = cat(3, projData_blocks{:});
else
% faster and more memory efficient version
proj_size = [size(projData_blocks{1},1),size(projData_blocks{1},2),sum(cellfun(@(x)size(x,3), projData_blocks))];
for ii = 1:10
try
projData = zeros(proj_size, 'single');
break
catch err
end
pause(1)
end
if ii == 10
warning('Unsufficient memory to allocate %3.2gGB RAM', prod(proj_size)*4/1e9)
utils.check_available_memory
rethrow(err)
end
offset = 0;
for ii = 1:Nblocks
tomo.set_to_array(projData, projData_blocks{ii}, offset);
offset = offset + size(projData_blocks{ii},3);
end
end
else
projData = projData_blocks{1};
end
end
+90
View File
@@ -0,0 +1,90 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% example script for ASTRA wrappers
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% 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
% volume settings
Npix_vol = [300, 300, 100] ;
Nangles = 400;
Npix_proj = [400, 400];
% create "data"
angles = linspace(0, 360, Nangles);
lamino_angle = 60;
volData = ones(Npix_vol, 'single');
% generate geometry
[cfg, vectors] = astra.ASTRA_initialize(Npix_vol, Npix_proj, angles, lamino_angle);
% find optimal split, for small volumes below 600^3 no split is needed
split = astra.ASTRA_find_optimal_split(cfg);
% generate projections
projData = astra.Ax_partial(volData, cfg, vectors, split);
figure(1)
% plot the projections
subplot(1,2,1)
plotting.imagesc3D(projData); axis off image; colormap bone
title('Angular geometry')
% do backprojection projections !!! not FBP !!!
backprojData = astra.Atx_partial(projData, cfg, vectors, split);
% generate rotation matrix, note that the expected angles needs to be
% adjusted to provide same results are the previous example
R3 = utils.get_rotation_matrix_3D((90-lamino_angle)*ones(Nangles,1), -angles, zeros(Nangles,1));
% generate geometry
[cfgR, vectorsR] = astra.ASTRA_initialize(Npix_vol, Npix_proj, R3);
% generate projections
projDataR = astra.Ax_partial(volData, cfgR, vectorsR, split);
figure(1)
% plot the projections
subplot(1,2,2)
% plot the projections
plotting.imagesc3D(projDataR); axis off image; colormap bone
title('Rotation matrix geometry')
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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.
+54
View File
@@ -0,0 +1,54 @@
% IRADON_GPU_WRAPPER back projector that allows to split the full volume
% into smaller pieces, tomography projector is based on ASTRA toolbox can be used
% either for data in RAM or on GPU (automatically decided from class of volData)
%
% vol = iradon_gpu_wrapper(sinogram, cfg, vectors)
%
% Inputs:
% **projData - array Nlayers x width_sinogram x Nangles of back-projected data
% **cfg - config structure generated by ASTRA_initialize
% **vectors - orientation of projections generated by ASTRA_initialize
% Outputs:
% ++vol - backprojected volume
%
% recompile commands
% (Linux) mexcuda -largeArrayDims -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 -largeArrayDims -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
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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.
function vol = iradon_gpu_wrapper(sinogram, cfg, vectors)
vol = ASTRA_GPU_wrapper('bp', sinogram, cfg, vectors);
end
+58
View File
@@ -0,0 +1,58 @@
% FUNCTION full_array = add_to_3D(full_array, small_array, position)
% add one small 3D block into large 3D array
% Inputs:
% full_array
% small_array
% position - offset from (1,1,1) coordinate in pixels
% *-----------------------------------------------------------------------*
% |                                                                       |
% |  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.
%
%
%
function full_array = add_to_3D(full_array, small_array, position)
position = round(position);
N_f = size(full_array);
N_s = size(small_array);
for i = 1:ndims(full_array)
ind_f{i} = unique(min(N_f(i),max(1,position(i)+(1:N_s(i)))));
ind_s{i} = unique(min(N_s(i),max(1,ind_f{i}-position(i))));
end
full_array(ind_f{:}) = full_array(ind_f{:}) + small_array(ind_s{:});
end
+51
View File
@@ -0,0 +1,51 @@
% FUNCTION sinogram = astra2matlab(sinogram)
% simple function to reshape sinogram from ASTRA (C++) order to Matlab (FORTRAN) order
% Inputs:
% sinogram - real value sinogram
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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
%
function sinogram = astra2matlab(sinogram)
% simple function to reshape sinogram from ASTRA (C++) order to Matlab (FORTRAN) order
if isnumeric(sinogram)
[U,V,A] = size(sinogram);
sinogram = permute(reshape(sinogram, [U,A,V]), [3,1,2]);
elseif iscell(sinogram)
for ii = 1:length(sinogram)
sinogram{ii} = astra2matlab(sinogram{ii});
end
end
end
+52
View File
@@ -0,0 +1,52 @@
% FUNCTION sinogram = astra2matlab(sinogram)
% simple function to reshape sinogram from Matlab (FORTRAN) order to ASTRA (C++) order
% Inputs:
% sinogram - real value sinogram
%*-----------------------------------------------------------------------*
%|                                                                       |
%|  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
%
function sinogram = matlab2astra(sinogram)
if isnumeric(sinogram)
[U,V,A] = size(sinogram);
sinogram = reshape(permute(sinogram, [2,3,1]),[U,V,A]);
elseif iscell(sinogram)
for ii = 1:length(sinogram)
sinogram{ii} = matlab2astra(sinogram{ii});
end
end
end