mirror of
https://github.com/c-sooyoung/fold_slice.git
synced 2026-09-17 21:39:08 +09:00
initial commit
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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 */
|
||||
@@ -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__ */
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user