mirror of
https://github.com/c-sooyoung/fold_slice.git
synced 2026-09-17 20:39:08 +09:00
initial commit
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
%ASTRA_GPU_WRAPPER wrapper around the astra toolkit, it automatically
|
||||
%recompiles the wrapper if some problems with the MEX file are detected
|
||||
%
|
||||
% varargout = ASTRA_GPU_wrapper(direction, input_array, cfg, vectors,output_array,varargin)
|
||||
%
|
||||
%
|
||||
% ** direction 'fp' or 'bp' - forward or backward projection operator
|
||||
% ** input_array either projected volume or backprojected projection array
|
||||
% ** cfg cfg structed created by ASTRA_initialize
|
||||
% ** vectors projection geometry created by ASTRA_initialize
|
||||
%
|
||||
% optional:
|
||||
% ** output_array either reconstructed volume or projected array
|
||||
% ** deformation_fields 3x2 or 3x1 cell array if deformation vector fields for nonrigid deformation tomography
|
||||
%
|
||||
%
|
||||
% returns:
|
||||
% ++ output resulting reconstruction, if output_array ~= [], result will
|
||||
% be written to output_array directly to avoid memory allocation
|
||||
|
||||
|
||||
|
||||
function varargout = ASTRA_GPU_wrapper(direction, input_array, cfg, vectors,varargin)
|
||||
varargout = cell(nargout,1);
|
||||
assert(ismember(direction, {'fp', 'bp'}), 'Wrong option')
|
||||
|
||||
try
|
||||
% call mex function
|
||||
[varargout{:}] = ASTRA_GPU_wrapper(direction, input_array, cfg, vectors,varargin{:});
|
||||
catch err
|
||||
warning(err.identifier, 'ASTRA wrapper returned the following error: %s', err.message)
|
||||
if any(strcmp(err.identifier, { 'MATLAB:UndefinedFunction','MATLAB:mex:ErrInvalidMEXFile'}))
|
||||
path = replace(mfilename('fullpath'), mfilename, '');
|
||||
utils.verbose(0, 'Trying to recompile the MEX function ... ')
|
||||
|
||||
mexcuda('-outdir',fullfile(path, 'private'), ...
|
||||
fullfile(path, 'ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu'), ...
|
||||
fullfile(path, 'ASTRA_GPU_wrapper/util3d.cu'), ...
|
||||
fullfile(path, 'ASTRA_GPU_wrapper/par3d_fp.cu'), ...
|
||||
fullfile(path, 'ASTRA_GPU_wrapper/par3d_bp.cu'));
|
||||
|
||||
[varargout{:}] = ASTRA_GPU_wrapper(direction, input_array, cfg, vectors,varargin{:});
|
||||
else
|
||||
utils.report_GPU_usage
|
||||
rethrow(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
|
||||
*-----------------------------------------------------------------------*
|
||||
| |
|
||||
| Except where otherwise noted, this work is licensed under a |
|
||||
| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
| International (CC BY-NC-SA 4.0) license. |
|
||||
| |
|
||||
| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
| |
|
||||
| Author: CXS group, PSI |
|
||||
*-----------------------------------------------------------------------*
|
||||
You may use this code with the following provisions:
|
||||
|
||||
If the code is fully or partially redistributed, or rewritten in another
|
||||
computing language this notice should be included in the redistribution.
|
||||
|
||||
If this code, or subfunctions or parts of it, is used for research in a
|
||||
publication or if it is fully or partially rewritten for another
|
||||
computing language the authors and institution should be acknowledged
|
||||
in written form in the publication: “Data processing was carried out
|
||||
using the “cSAXS matlab package” developed by the CXS group,
|
||||
Paul Scherrer Institut, Switzerland.”
|
||||
Variations on the latter text can be incorporated upon discussion with
|
||||
the CXS group if needed to more specifically reflect the use of the package
|
||||
for the published work.
|
||||
|
||||
A publication that focuses on describing features, or parameters, that
|
||||
are already existing in the code should be first discussed with the
|
||||
authors.
|
||||
|
||||
This code and subroutines are part of a continuous development, they
|
||||
are provided “as they are” without guarantees or liability on part
|
||||
of PSI or the authors. It is the user responsibility to ensure its
|
||||
proper use and the correctness of the results.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
// Defines the exported functions for the DLL application.
|
||||
//
|
||||
// recompile commands
|
||||
// (Linux, GCC 4.8.5) mexcuda -outdir private ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper/util3d.cu ASTRA_GPU_wrapper/par3d_fp.cu ASTRA_GPU_wrapper/par3d_bp.cu
|
||||
// (Windows) mexcuda -outdir private ASTRA_GPU_wrapper\ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper\util3d.cu ASTRA_GPU_wrapper\par3d_fp.cu ASTRA_GPU_wrapper\par3d_bp.cu
|
||||
|
||||
/************* INPUTS *****************************/
|
||||
/*
|
||||
string 'fp' or 'bp' - forward / backward projection
|
||||
single gpuArray volume or data object
|
||||
struct cfg - contain configuration for astra, created by ASTRA_initialize.m
|
||||
double array vec - contain projection geometry for astra, created by ASTRA_initialize.m
|
||||
(optional)
|
||||
single gpuArray - volume or data object to write the results to
|
||||
*/
|
||||
|
||||
|
||||
#include "cuda_runtime.h"
|
||||
#include "device_launch_parameters.h"
|
||||
#include <cuda.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
|
||||
#include "mex.h"
|
||||
#include "gpu/mxGPUArray.h"
|
||||
|
||||
#include "util3d.h"
|
||||
#include "dims3d.h"
|
||||
#include "par3d_bp.h"
|
||||
#include "par3d_fp.h"
|
||||
|
||||
|
||||
|
||||
void mexFunction(int nlhs, mxArray *plhs[],
|
||||
int nrhs, mxArray const *prhs[])
|
||||
{
|
||||
|
||||
//mexPrintf("Warning: loading development version of ASTRA\n");
|
||||
//mexPrintf("Ninputs:%i\n", nrhs);
|
||||
|
||||
if (!((nrhs == 4) || (nrhs == 5) || (nrhs == 8 ) || (nrhs == 11 ) ))
|
||||
mexErrMsgTxt("4,5, 8, or 11 input arguments required");
|
||||
|
||||
|
||||
using namespace astraCUDA3d;
|
||||
char const * const errId = "parallel:gpu:mexGPUExample:InvalidInput";
|
||||
char const * const errMsg = "Invalid input to MEX file.";
|
||||
|
||||
|
||||
/* Throw an error if the input is not a GPU array. */
|
||||
if (!mxIsGPUArray(prhs[1])) {
|
||||
mexErrMsgIdAndTxt(errId, "The second input must be GPU array");
|
||||
}
|
||||
|
||||
|
||||
/* Load configuration */
|
||||
SDimensions3D dims;
|
||||
mxArray * tmp;
|
||||
double * val;
|
||||
#define SETVAR(name) do {tmp = mxGetField(prhs[2], 0, ""#name""); if (tmp!=NULL) { val = mxGetPr(tmp); dims.name = (unsigned int)val[0]; }} while (0);
|
||||
SETVAR(iVolX);
|
||||
SETVAR(iVolY);
|
||||
SETVAR(iVolZ);
|
||||
SETVAR(iProjAngles);
|
||||
SETVAR(iProjU);
|
||||
SETVAR(iProjV);
|
||||
SETVAR(iRaysPerDetDim);
|
||||
SETVAR(iRaysPerVoxelDim);
|
||||
#undef SETVAR
|
||||
|
||||
|
||||
/* Initialize the MathWorks GPU API. */
|
||||
mxInitGPU();
|
||||
|
||||
|
||||
/* load confuguration of angles */
|
||||
double * my_angles = mxGetPr(prhs[3]);
|
||||
int Nangles = (int)mxGetM(prhs[3]);
|
||||
SPar3DProjection* angle = new SPar3DProjection[Nangles];
|
||||
|
||||
#define SETVAR(name,i,j) do { angle[i].name = my_angles[i+j*Nangles]; } while (0);
|
||||
for (int i = 0; i < Nangles; i++)
|
||||
{
|
||||
SETVAR(fRayX, i, 0);
|
||||
SETVAR(fRayY, i, 1);
|
||||
SETVAR(fRayZ, i, 2);
|
||||
SETVAR(fDetSX, i, 3);
|
||||
SETVAR(fDetSY, i, 4);
|
||||
SETVAR(fDetSZ, i, 5);
|
||||
SETVAR(fDetUX, i, 6);
|
||||
SETVAR(fDetUY, i, 7);
|
||||
SETVAR(fDetUZ, i, 8);
|
||||
SETVAR(fDetVX, i, 9);
|
||||
SETVAR(fDetVY, i, 10);
|
||||
SETVAR(fDetVZ, i, 11);
|
||||
// mexPrintf("---------------------- \n");
|
||||
}
|
||||
#undef SETVAR
|
||||
|
||||
|
||||
char * task = mxArrayToString(prhs[0]);
|
||||
|
||||
//mexPrintf("--------- Task %s \n ", task);
|
||||
|
||||
|
||||
/* Load input data */
|
||||
mxGPUArray const * m_data = mxGPUCreateFromMxArray(prhs[1]);
|
||||
if ((mxGPUGetClassID(m_data) != mxSINGLE_CLASS)) {
|
||||
mexErrMsgIdAndTxt(errId, errMsg);
|
||||
}
|
||||
float * p_data = (float *)mxGPUGetDataReadOnly(m_data);
|
||||
|
||||
DeformField DF;
|
||||
if (nrhs == 8 || nrhs == 11 ) {
|
||||
/* load deformation field */
|
||||
DF.use_deform = true;
|
||||
DF.use_linear_model = false; // assume contant deformation
|
||||
DF.X0 = mxGPUCreateFromMxArray(prhs[5]);
|
||||
DF.Y0 = mxGPUCreateFromMxArray(prhs[6]);
|
||||
DF.Z0 = mxGPUCreateFromMxArray(prhs[7]);
|
||||
if ((mxGPUGetClassID(DF.X0) != mxSINGLE_CLASS) |
|
||||
(mxGPUGetClassID(DF.Y0) != mxSINGLE_CLASS) |
|
||||
(mxGPUGetClassID(DF.Z0) != mxSINGLE_CLASS)) {
|
||||
mexPrintf("wrong input type: deformation fields has to be single\n");
|
||||
mexErrMsgIdAndTxt(errId, errMsg);
|
||||
}
|
||||
if (nrhs == 11 ) {
|
||||
DF.use_linear_model = true; // assume linear deformation
|
||||
DF.X1 = mxGPUCreateFromMxArray(prhs[8]);
|
||||
DF.Y1 = mxGPUCreateFromMxArray(prhs[9]);
|
||||
DF.Z1 = mxGPUCreateFromMxArray(prhs[10]);
|
||||
if ((mxGPUGetClassID(DF.X1) != mxSINGLE_CLASS) |
|
||||
(mxGPUGetClassID(DF.Y1) != mxSINGLE_CLASS) |
|
||||
(mxGPUGetClassID(DF.Z1) != mxSINGLE_CLASS)) {
|
||||
mexPrintf("wrong input type: deformation fields has to be single\n");
|
||||
mexErrMsgIdAndTxt(errId, errMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
DF.use_deform = false;
|
||||
|
||||
|
||||
|
||||
if (strcmp(task, "fp")==0)
|
||||
{
|
||||
//mexPrintf(" forward projection \n ");
|
||||
|
||||
/* make volume array (no copying) */
|
||||
cudaPitchedPtr volData;
|
||||
volData.ptr = p_data;
|
||||
volData.pitch = dims.iVolX * sizeof(float);
|
||||
volData.xsize = dims.iVolX;
|
||||
volData.ysize = dims.iVolY;
|
||||
|
||||
|
||||
mxGPUArray * m_projData;
|
||||
if(nrhs >= 5 && !mxIsEmpty(prhs[4]) )
|
||||
{
|
||||
/**** copy of the array is the slow operation and also GPU memory is limited *****/
|
||||
// m_projData = mxGPUCopyFromMxArray(prhs[4]);
|
||||
|
||||
/* Use ugly trick to write directly to the provided GPU array ...
|
||||
=> Now it is writting directly into the input field !!! DANGEROUS */
|
||||
|
||||
m_projData = const_cast<mxGPUArray*>(mxGPUCreateFromMxArray(prhs[4]));
|
||||
if ((mxGPUGetClassID(m_projData) != mxSINGLE_CLASS)) {
|
||||
mexPrintf("m_projData\n");
|
||||
mexErrMsgIdAndTxt(errId, errMsg);
|
||||
}
|
||||
const mwSize * projSize = mxGPUGetDimensions(m_projData);
|
||||
|
||||
if (dims.iProjU != projSize[0] ||
|
||||
dims.iProjV != projSize[1] ||
|
||||
dims.iProjAngles != projSize[2])
|
||||
mexErrMsgIdAndTxt(errId, "Wrong size of the inputs array");
|
||||
|
||||
//mexPrintf("Writting directly to the input array\n\n");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocate projection field */
|
||||
int const Ndim = 3;
|
||||
mwSize projSize[3];
|
||||
projSize[0] = (mwSize)dims.iProjU;
|
||||
projSize[1] = (mwSize)dims.iProjV;
|
||||
projSize[2] = (mwSize)dims.iProjAngles;
|
||||
m_projData = mxGPUCreateGPUArray(Ndim,
|
||||
projSize,
|
||||
mxSINGLE_CLASS,
|
||||
mxREAL,
|
||||
MX_GPU_INITIALIZE_VALUES);
|
||||
}
|
||||
|
||||
/* make cudaPitchedPtr for projection field */
|
||||
cudaPitchedPtr projData;
|
||||
projData.ptr = (float *)mxGPUGetData(m_projData);
|
||||
projData.pitch = dims.iProjU * sizeof(float);
|
||||
projData.xsize = dims.iProjU;
|
||||
projData.ysize = dims.iProjV;
|
||||
|
||||
//mexPrintf("astraCUDA3d::Par3DFP \n ") ;
|
||||
astraCUDA3d::Par3DFP(volData, projData, dims, angle, 1.0f, DF);
|
||||
checkLastError("After Projector");
|
||||
|
||||
|
||||
/* Wrap the result up as a MATLAB gpuArray for return. */
|
||||
if (nlhs > 0)
|
||||
plhs[0] = mxGPUCreateMxArrayOnGPU(m_projData);
|
||||
mxGPUDestroyGPUArray(m_projData);
|
||||
mxGPUDestroyGPUArray(m_data);
|
||||
|
||||
|
||||
}
|
||||
else if (strcmp(task, "bp")==0)
|
||||
{
|
||||
//mexPrintf(" backward projection \n ");
|
||||
|
||||
|
||||
/* make projection field (no copying) */
|
||||
cudaPitchedPtr projData;
|
||||
projData.ptr = p_data;
|
||||
projData.pitch = dims.iProjU * sizeof(float);
|
||||
projData.xsize = dims.iProjU;
|
||||
projData.ysize = dims.iProjAngles;
|
||||
mxGPUArray* m_volData;
|
||||
if(nrhs >= 5 && !mxIsEmpty(prhs[4]) )
|
||||
{
|
||||
/**** copy of the array is the slow operation and also GPU memory is limited *****/
|
||||
// m_volData = mxGPUCopyFromMxArray(prhs[4]);
|
||||
|
||||
/* Use ugly trick to write directly to the provided GPU array ...
|
||||
=> Now it is writting directly into the input field !!! DANGEROUS */
|
||||
|
||||
m_volData = const_cast<mxGPUArray*>(mxGPUCreateFromMxArray(prhs[4]));
|
||||
if ((mxGPUGetClassID(m_volData) != mxSINGLE_CLASS)) {
|
||||
mexPrintf("m_volData\n");
|
||||
mexErrMsgIdAndTxt(errId, errMsg);
|
||||
}
|
||||
mwSize volSize[3];
|
||||
const mwSize * volSize0 = mxGPUGetDimensions(m_volData);
|
||||
if (mxGPUGetNumberOfDimensions(m_volData)==3) {
|
||||
volSize[0]=volSize0[0];
|
||||
volSize[1]=volSize0[1];
|
||||
volSize[2]=volSize0[2];
|
||||
} else {
|
||||
volSize[0]=volSize0[0];
|
||||
volSize[1]=volSize0[1];
|
||||
volSize[2]=1;
|
||||
}
|
||||
|
||||
|
||||
if (dims.iVolX != volSize[0] ||
|
||||
dims.iVolY != volSize[1] ||
|
||||
dims.iVolZ != volSize[2])
|
||||
mexErrMsgIdAndTxt(errId, "Wrong size of the inputs array");
|
||||
|
||||
} else {
|
||||
/* allocate volume data */
|
||||
int const Ndim = 3;
|
||||
mwSize volSize[3];
|
||||
volSize[0] = (mwSize)dims.iVolX;
|
||||
volSize[1] = (mwSize)dims.iVolY;
|
||||
volSize[2] = (mwSize)dims.iVolZ;
|
||||
m_volData = mxGPUCreateGPUArray(Ndim,
|
||||
volSize,
|
||||
mxSINGLE_CLASS,
|
||||
mxREAL,
|
||||
MX_GPU_INITIALIZE_VALUES);
|
||||
}
|
||||
|
||||
/* make volume array pointer*/
|
||||
cudaPitchedPtr volData;
|
||||
volData.ptr = (float *)mxGPUGetData(m_volData);
|
||||
volData.pitch = dims.iVolX * sizeof(float);
|
||||
volData.xsize = dims.iVolX;
|
||||
volData.ysize = dims.iVolY;
|
||||
|
||||
astraCUDA3d::Par3DBP(volData, projData, dims, angle, 1.0f, DF);
|
||||
checkLastError("After Projector");
|
||||
|
||||
|
||||
/* Wrap the result up as a MATLAB gpuArray for return. */
|
||||
if (nlhs > 0)
|
||||
plhs[0] = mxGPUCreateMxArrayOnGPU(m_volData);
|
||||
mxGPUDestroyGPUArray(m_volData);
|
||||
mxGPUDestroyGPUArray(m_data);
|
||||
|
||||
}
|
||||
else
|
||||
mexPrintf("No such option");
|
||||
|
||||
if (DF.use_deform) {
|
||||
//mexPrintf("Deleted DF");
|
||||
mxGPUDestroyGPUArray(DF.X0);
|
||||
mxGPUDestroyGPUArray(DF.Y0);
|
||||
mxGPUDestroyGPUArray(DF.Z0);
|
||||
if (DF.use_linear_model) {
|
||||
mxGPUDestroyGPUArray(DF.X1);
|
||||
mxGPUDestroyGPUArray(DF.Y1);
|
||||
mxGPUDestroyGPUArray(DF.Z1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
-----------------------------------------------------------------------
|
||||
Copyright: 2010-2015, iMinds-Vision Lab, University of Antwerp
|
||||
2014-2015, CWI, Amsterdam
|
||||
|
||||
Contact: astra@uantwerpen.be
|
||||
Website: http://sf.net/projects/astra-toolbox
|
||||
|
||||
This file is part of the ASTRA Toolbox.
|
||||
|
||||
|
||||
The ASTRA Toolbox is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
The ASTRA Toolbox is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with the ASTRA Toolbox. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
$Id$
|
||||
*/
|
||||
|
||||
#ifndef _INC_ASTRA_GEOMETRYUTIL3D
|
||||
#define _INC_ASTRA_GEOMETRYUTIL3D
|
||||
|
||||
namespace astra {
|
||||
|
||||
struct SConeProjection {
|
||||
// the source
|
||||
double fSrcX, fSrcY, fSrcZ;
|
||||
|
||||
// the origin ("bottom left") of the (flat-panel) detector
|
||||
double fDetSX, fDetSY, fDetSZ;
|
||||
|
||||
// the U-edge of a detector pixel
|
||||
double fDetUX, fDetUY, fDetUZ;
|
||||
|
||||
// the V-edge of a detector pixel
|
||||
double fDetVX, fDetVY, fDetVZ;
|
||||
|
||||
|
||||
|
||||
|
||||
void translate(double dx, double dy, double dz) {
|
||||
fSrcX += dx;
|
||||
fSrcY += dy;
|
||||
fSrcZ += dz;
|
||||
fDetSX += dx;
|
||||
fDetSY += dy;
|
||||
fDetSZ += dz;
|
||||
|
||||
}
|
||||
void scale(double factor) {
|
||||
fSrcX *= factor;
|
||||
fSrcY *= factor;
|
||||
fSrcZ *= factor;
|
||||
fDetSX *= factor;
|
||||
fDetSY *= factor;
|
||||
fDetSZ *= factor;
|
||||
fDetUX *= factor;
|
||||
fDetUY *= factor;
|
||||
fDetUZ *= factor;
|
||||
fDetVX *= factor;
|
||||
fDetVY *= factor;
|
||||
fDetVZ *= factor;
|
||||
}
|
||||
};
|
||||
|
||||
struct SPar3DProjection {
|
||||
// the ray direction
|
||||
double fRayX, fRayY, fRayZ;
|
||||
|
||||
// the origin ("bottom left") of the (flat-panel) detector
|
||||
double fDetSX, fDetSY, fDetSZ;
|
||||
|
||||
// the U-edge of a detector pixel
|
||||
double fDetUX, fDetUY, fDetUZ;
|
||||
|
||||
// the V-edge of a detector pixel
|
||||
double fDetVX, fDetVY, fDetVZ;
|
||||
|
||||
|
||||
|
||||
|
||||
void translate(double dx, double dy, double dz) {
|
||||
fDetSX += dx;
|
||||
fDetSY += dy;
|
||||
fDetSZ += dz;
|
||||
}
|
||||
void scale(double factor) {
|
||||
fRayX *= factor;
|
||||
fRayY *= factor;
|
||||
fRayZ *= factor;
|
||||
fDetSX *= factor;
|
||||
fDetSY *= factor;
|
||||
fDetSZ *= factor;
|
||||
fDetUX *= factor;
|
||||
fDetUY *= factor;
|
||||
fDetUZ *= factor;
|
||||
fDetVX *= factor;
|
||||
fDetVY *= factor;
|
||||
fDetVZ *= factor;
|
||||
}
|
||||
};
|
||||
|
||||
void computeBP_UV_Coeffs(const SPar3DProjection& proj,
|
||||
double &fUX, double &fUY, double &fUZ, double &fUC,
|
||||
double &fVX, double &fVY, double &fVZ, double &fVC);
|
||||
|
||||
void computeBP_UV_Coeffs(const SConeProjection& proj,
|
||||
double &fUX, double &fUY, double &fUZ, double &fUC,
|
||||
double &fVX, double &fVY, double &fVZ, double &fVC,
|
||||
double &fDX, double &fDY, double &fDZ, double &fDC);
|
||||
|
||||
|
||||
SConeProjection* genConeProjections(unsigned int iProjAngles,
|
||||
unsigned int iProjU,
|
||||
unsigned int iProjV,
|
||||
double fOriginSourceDistance,
|
||||
double fOriginDetectorDistance,
|
||||
double fDetUSize,
|
||||
double fDetVSize,
|
||||
const float *pfAngles);
|
||||
|
||||
SPar3DProjection* genPar3DProjections(unsigned int iProjAngles,
|
||||
unsigned int iProjU,
|
||||
unsigned int iProjV,
|
||||
double fDetUSize,
|
||||
double fDetVSize,
|
||||
const float *pfAngles);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
@@ -0,0 +1,103 @@
|
||||
% ASTRA_FIND_OPTIMAL_SPLIT Find optimal split of data and make the blocks sufficiently small for
|
||||
% limited GPU memory
|
||||
%
|
||||
% split = ASTRA_find_optimal_split(cfg, num_gpu, angle_blocks, propagator)
|
||||
%
|
||||
% Inputs:
|
||||
% **cfg - config structure generated by ASTRA_initialize
|
||||
% **num_gpu - number of gpu to split the data
|
||||
% **angle_blocks - number of angular blocks (ie in SART method or FSC)
|
||||
% **propagator - which propagator should be assumed : FWD, BACK, both (default)
|
||||
% Outputs:
|
||||
% ++split - volume / angle split - [split_x,split_y,split_z,split_angles]
|
||||
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
|
||||
|
||||
function split = ASTRA_find_optimal_split(cfg, num_gpu, angle_blocks, propagator)
|
||||
|
||||
if gpuDeviceCount == 0
|
||||
split = 1;
|
||||
return
|
||||
end
|
||||
|
||||
gpu = gpuDevice;
|
||||
if nargin < 2 || num_gpu == 0
|
||||
num_gpu = 1;
|
||||
end
|
||||
if nargin < 3
|
||||
angle_blocks = 1;
|
||||
end
|
||||
if nargin < 4
|
||||
propagator = 'both';
|
||||
end
|
||||
|
||||
Nangles = cfg.iProjAngles / angle_blocks;
|
||||
if isfield(cfg, 'Grouping')
|
||||
Nangles = min(Nangles, cfg.Grouping);
|
||||
end
|
||||
|
||||
split = [1,1,1];
|
||||
if ismember(lower(propagator), {'fwd','both'})
|
||||
split = max(split, ceil([cfg.iVolX, cfg.iVolY, cfg.iVolZ] / 4096 )); % texture memory limit
|
||||
split = max(split, [1,1,ceil( (cfg.iVolX*cfg.iVolY*cfg.iVolZ*4) / 1.024e9 / prod(split)/num_gpu)]); % texture memory limit
|
||||
end
|
||||
split = max(split, [1,1,split(3)*ceil( ((cfg.iVolX*cfg.iVolY*cfg.iVolZ*4)/ prod(split)/num_gpu) / (gpu.AvailableMemory/2 - min(1.1e9, cfg.iVolX*cfg.iVolY*cfg.iVolZ*4)) )]); % gpu memory limit
|
||||
split = max(split, [1,1,split(3)*ceil( ((cfg.iVolX*cfg.iVolY*cfg.iVolZ )/ prod(split)/num_gpu) / double(intmax('int32')) )]); % maximal array on GPU limit
|
||||
|
||||
if ismember(lower(propagator), {'back','both'})
|
||||
% if projection would be larger than 4096x4096 -> split the reconstruction volume
|
||||
split = max(split, [cfg.iProjU, cfg.iProjU, cfg.iProjV]/4096 ); % texture memory limit
|
||||
end
|
||||
|
||||
% projection size limitation + astra allows only < 1024 angles
|
||||
split(4) = max(ceil(Nangles/1024), ceil( (cfg.iProjU*cfg.iProjV*min(1024,Nangles)*4) / gpu.TotalMemory/num_gpu)); % gpu memory limit
|
||||
|
||||
% RAM limits
|
||||
if cfg.iVolX*cfg.iVolY*cfg.iVolZ > 2e6
|
||||
freemem = 0.8 * utils.check_available_memory;
|
||||
split(4) = max(split(4), num_gpu*(4*(cfg.iProjU*cfg.iProjV*cfg.iProjAngles...
|
||||
+ cfg.iVolX*cfg.iVolY*cfg.iVolZ*(1+1/prod(split(1:3)))))...
|
||||
/ (freemem * 1e6) ); % RAM memory limit
|
||||
end
|
||||
split(4) = ceil(split(4));
|
||||
split(1:3) = 2.^nextpow2(split(1:3));
|
||||
% if any(split ~= 1)
|
||||
% fprintf('Automatically splitting to %ix%ix%ix(%i) cubes \n', split);
|
||||
% end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,299 @@
|
||||
% ASTRA_INITIALIZE Generate inputs needed for astra MEX wrapper
|
||||
%
|
||||
% [cfg, vectors] = ASTRA_initialize(Npix, size_projection,angles,lamino_angle, tilt_angle, pixel_scale, rotation_center)
|
||||
%
|
||||
% Inputs for angular geometry (!! all angles are expected in degress !!):
|
||||
% **Npix - size of tomogram
|
||||
% **size_projection - size of sinogram (Nlayers, width, Nangles)
|
||||
% **angles - rotation angles of projections in degrees
|
||||
% *optional*:
|
||||
% **lamino_angle - laminography angle / angles in degrees. lamino_angle ==
|
||||
% 90 is standard tomography , default = 90
|
||||
% **tilt_angle - tilt of camera with respect to the rotation axis coordinates, in degrees, default = 0
|
||||
% **pixel_scale - scale of pixels in tomogram compares to the
|
||||
% projection pixel size, default = 1
|
||||
% **rotation_center - center of rotation coordinates, default = size_projection/2
|
||||
% **skewness_angle - distorsion of parallel axis by [1, sind(alpha); 0, 1]
|
||||
%
|
||||
% Inputs for rotation matrix geometry:
|
||||
% **Npix - size of tomogram
|
||||
% **size_projection - size of sinogram (Nlayers, width, Nangles)
|
||||
% **rotation_matrix - R is a 3x3xn matrix, for n projections
|
||||
%
|
||||
% Outputs:
|
||||
% ++cfg - config structure for ASTRA mex wrapper
|
||||
% ++vectors - parameter vector for each angle
|
||||
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function [cfg, vectors] = ASTRA_initialize(Npix, size_projection, varargin)
|
||||
use_rotmat = length(varargin) == 1 && size(varargin{1},1) == 3 && size(varargin{1},2) == 3 ;
|
||||
if use_rotmat
|
||||
% rotation matrices were provided
|
||||
rot_mat = varargin{1};
|
||||
Nangles = size(rot_mat,3);
|
||||
r.rotation_center = size_projection/2; % only centered geometry is supported when rotation matrix is provided
|
||||
else
|
||||
% angles and other parameters were provided
|
||||
par = inputParser;
|
||||
par.KeepUnmatched = true;
|
||||
%% ALL angles are assumed in degrees
|
||||
par.addRequired('angles') % rotation angles of projections in degrees
|
||||
par.addOptional('lamino_angle', 90, @isnumeric) % laminography angle / angles in degrees. lamino_angle == 90 is standard tomography , default = 90
|
||||
par.addOptional('tilt_angle', 0, @isnumeric) % tilt of camera with respect to the rotation axis coordinates, in
|
||||
par.addOptional('pixel_scale', [1,1], @isnumeric) % scale of pixels in tomogram compares to the projection pixel size, default = 1
|
||||
par.addOptional('rotation_center', size_projection/2, @isnumeric) % center of rotation cooridinates, default = size_projection/2
|
||||
par.addOptional('skewness_angle', 0) % distorsion of parallel axis by [1, sind(alpha); 0, 1]
|
||||
par.addParameter('show_geometry', false) % plot also a geometry for each projection
|
||||
par.parse(varargin{:})
|
||||
r = par.Results;
|
||||
Nangles = length(r.angles);
|
||||
end
|
||||
|
||||
|
||||
% angles should be sorted in order to maximize performance of the astra
|
||||
% toolbox -> better use of texture memory
|
||||
|
||||
assert(math.isint(Npix), 'Npix is not integer');
|
||||
assert(math.isint(size_projection), 'size_projection is not integer');
|
||||
|
||||
if isscalar(Npix)
|
||||
Npix(2) = Npix;
|
||||
end
|
||||
if length(Npix) == 2 && all(r.lamino_angle == 90)
|
||||
Npix(3) = size_projection(1); % default behaviour is to have same number of layers in reconstruction and in laminography
|
||||
elseif length(Npix) == 2 && any(r.lamino_angle ~= 90)
|
||||
error('All three dimensions of the volume size has to be specified for the laminograhy geometry')
|
||||
end
|
||||
|
||||
cfg.iVolX = Npix(1);
|
||||
cfg.iVolY = Npix(2);
|
||||
cfg.iVolZ = Npix(3);
|
||||
cfg.iProjAngles = Nangles;
|
||||
cfg.iProjU = size_projection(2);
|
||||
cfg.iProjV = size_projection(1);
|
||||
cfg.iRaysPerDet = 1;
|
||||
cfg.iRaysPerDetDim = 1;
|
||||
cfg.iRaysPerVoxelDim = 1;
|
||||
source_distance = 1; % currenlty not implemented in the ASTRA wrapper
|
||||
|
||||
|
||||
if use_rotmat
|
||||
[vectors] = astra_convert_R_vectors(rot_mat,Nangles);
|
||||
else
|
||||
% compatibility with iradonfast
|
||||
r.angles = r.angles + 90;
|
||||
cfg.lamino_angle = r.lamino_angle;
|
||||
cfg.pixel_scale = r.pixel_scale;
|
||||
cfg.tilt_angle = r.tilt_angle;
|
||||
cfg.skewness_angle = r.skewness_angle;
|
||||
vectors = ASTRA_get_geometry(r.angles, r.lamino_angle, r.tilt_angle, source_distance,r.pixel_scale,r.skewness_angle,r.show_geometry);
|
||||
end
|
||||
|
||||
%%%% apply geometry correction to shift reconstruction into center %%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
vectors(:,4:6) = vectors(:,4:6) -(vectors(:,10:12).*(r.rotation_center(:,1) )+vectors(:,7:9).*(r.rotation_center(:,2) ));
|
||||
|
||||
end
|
||||
|
||||
function vectors = ASTRA_get_geometry(angles, lamino_angle, tilt_angle, source_distance,pixel_scale,skewness_angle,show)
|
||||
|
||||
Nangles = numel(angles);
|
||||
% angles should be sorted in order to maximize performance of the astra
|
||||
% toolbox -> better use of texture memory
|
||||
|
||||
angles = deg2rad(angles(:));
|
||||
lamino_angle = pi/2 - deg2rad(lamino_angle);
|
||||
tilt_angle = deg2rad(tilt_angle);
|
||||
skewness_angle = deg2rad(skewness_angle);
|
||||
|
||||
if isscalar(lamino_angle)
|
||||
lamino_angle = lamino_angle .* ones(Nangles,1);
|
||||
end
|
||||
if isscalar(tilt_angle)
|
||||
tilt_angle = tilt_angle .* ones(Nangles,1);
|
||||
end
|
||||
if isscalar(skewness_angle)
|
||||
skewness_angle = skewness_angle .* ones(Nangles,1);
|
||||
end
|
||||
if isscalar(pixel_scale) || numel(pixel_scale) == 2
|
||||
pixel_scale = bsxfun(@times, pixel_scale , ones(Nangles,2));
|
||||
end
|
||||
|
||||
% We generate the same geometry as the circular one above.
|
||||
vectors = zeros(Nangles, 12);
|
||||
% ray direction
|
||||
vectors(:,1) = sin(angles).*cos(lamino_angle);
|
||||
vectors(:,2) = -cos(angles).*cos(lamino_angle);
|
||||
vectors(:,3) = sin(lamino_angle);
|
||||
|
||||
vectors(:,1:3) = vectors(:,1:3) .*source_distance;
|
||||
% center of detector
|
||||
vectors(:,4:6) = 0;
|
||||
% vector from detector pixel (0,0) to (0,1)
|
||||
vectors(:,7) = cos(angles)./pixel_scale(:,1);
|
||||
vectors(:,8) = sin(angles)./pixel_scale(:,1);
|
||||
vectors(:,9) = 0/pixel_scale(:,1);
|
||||
|
||||
% vector from detector pixel (0,0) to (1,0)
|
||||
|
||||
% cross(vectors(i,1:3), vectors(i,7:9))
|
||||
% dot(vectors(i,1:3), vectors(i,7:9))
|
||||
|
||||
vectors(:,10) = - sin(lamino_angle).*sin(angles)./pixel_scale(:,2);
|
||||
vectors(:,11) = sin(lamino_angle).*cos(angles)./pixel_scale(:,2);
|
||||
vectors(:,12) = cos(lamino_angle)./pixel_scale(:,2);
|
||||
|
||||
% Rodrigues' rotation formula - rotate detector in plane
|
||||
% perpendicular to the beam axis
|
||||
if any(tilt_angle ~= 0)
|
||||
for i = 1:Nangles
|
||||
vectors(i,7:9)=vectors(i,7:9).*cos(tilt_angle(i)) + ...
|
||||
cross(vectors(i,1:3), vectors(i,7:9)).*sin(tilt_angle(i)) + ...
|
||||
(vectors(i,1:3)*dot(vectors(i,1:3),vectors(i,7:9))).*(1-cos(tilt_angle(i)));
|
||||
vectors(i,10:12)=vectors(i,10:12).*cos(tilt_angle(i)) + ...
|
||||
cross(vectors(i,1:3), vectors(i,10:12)).*sin(tilt_angle(i)) + ...
|
||||
(vectors(i,1:3).*dot(vectors(i,1:3),vectors(i,10:12))).*(1-cos(tilt_angle(i)));
|
||||
end
|
||||
end
|
||||
|
||||
% search also for skewness => the same as rotation, but rotate
|
||||
% only one axis of the detector !!
|
||||
if any(skewness_angle ~= 0)
|
||||
for i = 1:Nangles
|
||||
vectors(i,10:12)=vectors(i,10:12).*cos(skewness_angle(i)/2) + ...
|
||||
cross(vectors(i,1:3), vectors(i,10:12)).*sin(skewness_angle(i)/2) + ...
|
||||
(vectors(i,1:3).*dot(vectors(i,1:3),vectors(i,10:12))).*(1-cos(skewness_angle(i)/2));
|
||||
end
|
||||
end
|
||||
|
||||
%% PLOT THE CURRENT SETUP
|
||||
|
||||
if show
|
||||
for i = 1:Nangles
|
||||
draw_projection_geometry(vectors(i,:))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
function [vectors] = astra_convert_R_vectors(rot_mat,Nangles)
|
||||
|
||||
% R is defined as in the arbitrary projection code by Manuel
|
||||
% This integrates along z (2nd index) and so this code follows this
|
||||
% convention.
|
||||
pixel_scale(1:Nangles,1:2) = [1];
|
||||
|
||||
|
||||
convert_matrix = [0 0 1
|
||||
0 1 0
|
||||
1 0 0];
|
||||
|
||||
for ii=1:size(rot_mat,3)
|
||||
rot_mat(:,:,ii)=rot_mat(:,:,ii)*convert_matrix;
|
||||
end
|
||||
|
||||
vectors = zeros(Nangles, 12);
|
||||
for i = 1:Nangles
|
||||
% before starting to mess around: works with a correction matrix
|
||||
% with the magnetic contrast, but not for the laminography
|
||||
vectors(i,1) = -rot_mat(3,1,i);
|
||||
vectors(i,2) = -rot_mat(3,3,i);
|
||||
vectors(i,3) = -rot_mat(3,2,i);
|
||||
|
||||
vectors(i,4:6) = 0;
|
||||
|
||||
vectors(i,7) = rot_mat(1,1,i)/pixel_scale(i,1);
|
||||
vectors(i,8) = rot_mat(1,3,i)/pixel_scale(i,1);
|
||||
vectors(i,9) = rot_mat(1,2,i)/pixel_scale(i,1);
|
||||
|
||||
vectors(i,10) = rot_mat(2,1,i)/pixel_scale(i,2);
|
||||
vectors(i,11) = rot_mat(2,3,i)/pixel_scale(i,2);
|
||||
vectors(i,12) = rot_mat(2,2,i)/pixel_scale(i,2);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function draw_projection_geometry(vectors)
|
||||
% show geometry saved in the "vectors" matrix
|
||||
ray = vectors(1:3);
|
||||
c_center = vectors(4:6)-ray;
|
||||
c_origin = c_center - vectors( 7:9)/2-vectors( 10:12)/2;
|
||||
|
||||
k = 6;
|
||||
n = 2^k-1;
|
||||
[x,y,z] = sphere(n);
|
||||
c = hadamard(2^k);
|
||||
s = 0.5;
|
||||
figure(15)
|
||||
surf(vectors(4)+s*x,vectors(5)+s*y,vectors(6)+s*z,c);
|
||||
shading flat
|
||||
colormap([1 1 0; 0 1 1])
|
||||
|
||||
hold all
|
||||
plot3d_vec(c_origin, vectors( 7:9), 'r');
|
||||
plot3d_vec(c_origin, vectors( 10:12), 'r');
|
||||
plot3d_vec(c_origin+vectors( 7:9), vectors( 10:12), 'r');
|
||||
plot3d_vec(c_origin+vectors( 10:12), vectors( 7:9), 'r');
|
||||
% draw "pixels" on a 10x10 grid
|
||||
for x = linspace(0,1,10)
|
||||
plot3d_vec(c_origin+x*vectors( 10:12), vectors( 7:9), 'r:');
|
||||
plot3d_vec(c_origin+x*vectors( 7:9),vectors( 10:12), 'r:');
|
||||
end
|
||||
plotting.mArrow3(c_center+ray*2,c_center, 'color', 'blue', 'stemWidth',0.02,'facealpha',0.5);
|
||||
|
||||
hold off
|
||||
axis([-1,1,-1,1,-1,1])
|
||||
drawnow
|
||||
|
||||
end
|
||||
|
||||
function h_out = plot3d_vec(x0, vec, varargin)
|
||||
h = plot3(x0(1)+[0,vec(1)], ...
|
||||
x0(2)+[0,vec(2)], ...
|
||||
x0(3)+[0,vec(3)], varargin{:});
|
||||
if nargout > 1
|
||||
h_out = h;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -0,0 +1,339 @@
|
||||
% AX_PARTIAL backprojector that allows to split the full volume into smaller pieces
|
||||
% composed tomography back-projector based on ASTRA toolbox
|
||||
% can be used either for data in RAM or on GPU (automatically decided from class of volData)
|
||||
% * volume is split based on "split" parameter, 1 == no splitting
|
||||
% * Ax_partial tries to split data if GPU limits are exceeded (ie texture memory limits)
|
||||
%
|
||||
% vol_full = Atx_partial(projData, cfg, vectors,split,varargin)
|
||||
%
|
||||
% Inputs:
|
||||
% **projData - array Nx x Ny x Nz of projected volume
|
||||
% **cfg - config structure generated by ASTRA_initialize
|
||||
% **vectors - orientation of projections generated by ASTRA_initialize
|
||||
% **split - 3 or 4 elements vector, [split X, split Y, split Z, split angle ]
|
||||
% *optional*
|
||||
% **deformation_fields - 3x1 cell contaning 3D arrays of local deformation of the object
|
||||
% **GPU - GPU id to be used for reconstruction
|
||||
% **verbose - verbose = 0 : (default) quiet, verbose = 1: standard info , verbose = 2: debug
|
||||
% **keep_on_GPU - if true keep reconstructed volume on GPU to make is faster, default == false (the safe option)
|
||||
%
|
||||
% *returns*
|
||||
% ++vol_full - projection of volData
|
||||
%
|
||||
% recompile commands
|
||||
% (Linux, GCC 4.8.5) mexcuda -outdir private ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper/util3d.cu ASTRA_GPU_wrapper/par3d_fp.cu ASTRA_GPU_wrapper/par3d_bp.cu
|
||||
% (Windows) mexcuda -outdir private ASTRA_GPU_wrapper\ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper\util3d.cu ASTRA_GPU_wrapper\par3d_fp.cu ASTRA_GPU_wrapper\par3d_bp.cu
|
||||
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
|
||||
function vol_full = Atx_partial(projData, cfg, vectors,split,varargin)
|
||||
|
||||
|
||||
import utils.*
|
||||
import math.*
|
||||
|
||||
par = inputParser;
|
||||
par.KeepUnmatched = true;
|
||||
|
||||
par.addOptional('deformation_fields', {}) % deformation_fields: 3x1 cell contaning 3D arrays of local deformation of the object
|
||||
par.addOptional('GPU', []) % GPUs id to be used in reconstruction
|
||||
par.addOptional('keep_on_GPU', false) % true - keep reconstructed volume on GPU to make is faster
|
||||
par.addOptional('verbose', 0) %
|
||||
|
||||
par.parse(varargin{:})
|
||||
r = par.Results;
|
||||
|
||||
if isempty(r.deformation_fields); r.deformation_fields = {}; end
|
||||
|
||||
%% check the inputs + check memory availibility on GPU
|
||||
assert(gpuDeviceCount>0, 'No CUDA enabled GPU availible')
|
||||
|
||||
if ~( (isa(projData, 'gpuArray') && strcmp(classUnderlying(projData), 'single')) || ...
|
||||
isa(projData, 'single') ) || ~isreal(projData)
|
||||
error('Only single precision real input array supported')
|
||||
end
|
||||
|
||||
if ~isempty(r.deformation_fields)
|
||||
assert(any(numel(r.deformation_fields) == [3,6]), 'Deformation field expected as 3x1 or 6x1 cell array')
|
||||
|
||||
for i = 1:numel(r.deformation_fields)
|
||||
if ~( (isa(r.deformation_fields{i}, 'gpuArray') && strcmp(classUnderlying(r.deformation_fields{i}), 'single')) || ...
|
||||
isa(r.deformation_fields{i}, 'single'))
|
||||
error('Only single precision for deformation fields is supported')
|
||||
end
|
||||
r.deformation_fields{i} = gpuArray(r.deformation_fields{i}); % move on GPU, they are usually small
|
||||
end
|
||||
r.deformation_fields = r.deformation_fields' ; % transpose to that array(:) results in sorted field
|
||||
end
|
||||
|
||||
|
||||
projSize = size(projData);
|
||||
cfg.iProjAngles = size(vectors,1); % ignore value in config
|
||||
|
||||
gpu = gpuDevice();
|
||||
if ~isempty(r.GPU) && gpu.Index ~= r.GPU(1)
|
||||
% switch and !! reset !! GPU
|
||||
if isa(projData, 'gpuArray'), error('Switching GPUs will reset content'); end
|
||||
gpu = gpuDevice(r.GPU(1));
|
||||
end
|
||||
|
||||
|
||||
assert(cfg.iVolX*cfg.iVolY*cfg.iVolZ > 0, 'Volume is empty');
|
||||
assert(numel(projSize) > 0, 'Projections are empty');
|
||||
|
||||
% input parameters check
|
||||
if ismatrix(projData); projSize = [projSize,1]; end
|
||||
assert(max(cfg.iProjU, cfg.iProjV) <= 4096, 'Sinogram exceed maximal size allowed by GPU (4096)')
|
||||
assert(all(projSize==[cfg.iProjV,cfg.iProjU,cfg.iProjAngles]), 'Wrong inputs size')
|
||||
assert(all(size(vectors)==[cfg.iProjAngles,12]), 'Wrong vectors size')
|
||||
|
||||
split_projections = cfg.iProjAngles > 1024 || ...
|
||||
numel(projData)*4 > 1024e6 || ... ; % Data array is to large, try splitting
|
||||
(length(split) > 3 && split(4) > 1); % split contains also angular split
|
||||
|
||||
% be sure that ASTRA wrapper is feeded by doubles !!
|
||||
for i = fieldnames(cfg)'
|
||||
cfg.(i{1}) = double(cfg.(i{1}));
|
||||
end
|
||||
vectors = double(vectors);
|
||||
split=double(split);
|
||||
|
||||
if all(split == 1) && ~split_projections
|
||||
assert(cfg.iVolX*cfg.iVolY*cfg.iVolZ*4 < min(4*double(intmax('int32')),gpu.AvailableMemory), 'Volume array is too large for GPU, use Atx_sup_partial');
|
||||
%% in the simplest case call ASTRA_GPU_wrapper directly
|
||||
if isa(projData, 'gpuArray'), r.keep_on_GPU = true; end % assume that the data should stay on GPU if provided
|
||||
projData = matlab2astra(gpuArray(projData));
|
||||
vol_full = astra.ASTRA_GPU_wrapper('bp',projData, cfg, vectors,[],r.deformation_fields{:});
|
||||
if ~r.keep_on_GPU; vol_full = gather(vol_full); end
|
||||
return
|
||||
end
|
||||
|
||||
%% otherwise prepare data for split and call ASTRA_GPU_wrapper on subvolumes
|
||||
|
||||
split = ceil(max(1,split));
|
||||
if isscalar(split)
|
||||
split = split .* ones(3,1);
|
||||
end
|
||||
|
||||
if length(split) < 4
|
||||
split(4) = 1;
|
||||
end
|
||||
|
||||
|
||||
%% PREPARE VOLUME %%
|
||||
|
||||
Npix_full = [cfg.iVolX,cfg.iVolY,cfg.iVolZ];
|
||||
Npix_small = Npix_full(:)./reshape(split(1:3),[],1);
|
||||
|
||||
cfg.iVolX = Npix_small(1);
|
||||
cfg.iVolY = Npix_small(2);
|
||||
cfg.iVolZ = Npix_small(3);
|
||||
|
||||
assert( all(mod(Npix_small,1)==0), sprintf('Volume array cannot be divided to %i %i %i cubes', split))
|
||||
assert(prod(Npix_small)*4 < min(4*double(intmax('int32')),gpu.AvailableMemory), 'Volume array is too large for GPU, use Atx_sup_partial');
|
||||
|
||||
|
||||
req_vol_memory = 4*(cfg.iVolX*cfg.iVolY*cfg.iVolZ) * (1+any(split(1:3)>1));
|
||||
|
||||
keep_volume_on_GPU = isa(projData, 'gpuArray') ||...
|
||||
(gpu.AvailableMemory * 0.5 > req_vol_memory) && ...
|
||||
( (cfg.iVolX*cfg.iVolY*cfg.iVolZ) < intmax('int32') );
|
||||
|
||||
% preallocate large array for results
|
||||
if keep_volume_on_GPU || prod(split(1:3)) == 1
|
||||
% transfer to GPU now
|
||||
vol_full = gpuArray.zeros(Npix_full, 'single');
|
||||
else
|
||||
% keep the volume in RAM
|
||||
vol_full = zeros(Npix_full, 'single');
|
||||
end
|
||||
|
||||
%% PREPARE PROJECTIONS %%
|
||||
|
||||
% fix if the number of projections is > 1024, or arrays are too large
|
||||
Nproj_groups = max(split(4),ceil(max([cfg.iProjAngles/1024, ...
|
||||
cfg.iProjU*cfg.iProjV*cfg.iProjAngles*4 / min(1024e6,gpu.AvailableMemory-1.024e9), ...
|
||||
cfg.iProjU * cfg.iProjV * cfg.iProjAngles / double(intmax('int32'))])));
|
||||
|
||||
% avoid some rounding issues during splitting
|
||||
Nproj_groups = ceil(cfg.iProjAngles / floor(cfg.iProjAngles/Nproj_groups));
|
||||
|
||||
|
||||
if r.verbose > 1
|
||||
fprintf('Size of the full volume: %i %i %i\n', Npix_full)
|
||||
fprintf('Size of the subvolume: %i %i %i\n', Npix_small)
|
||||
fprintf('Size of the data: %i %i %i\n', size(projData))
|
||||
fprintf('Free GPU memory: %3.2g%%\n', gpu.AvailableMemory/gpu.TotalMemory*100)
|
||||
end
|
||||
|
||||
if Nproj_groups > 1
|
||||
% fix if the number of projections is > 1024 (limitation of the ASTRA code )
|
||||
% also if the dataset is too large (>1024MB), do automatic splitting along angles
|
||||
|
||||
for i = 1:Nproj_groups
|
||||
ind = (1+(i-1)*ceil(cfg.iProjAngles/Nproj_groups)):i*ceil(cfg.iProjAngles/Nproj_groups);
|
||||
ind = ind(ind <= cfg.iProjAngles);
|
||||
proj_ind{i} = ind;
|
||||
vectors_tmp{i} = vectors(ind,:);
|
||||
cfg_tmp{i} = cfg;
|
||||
cfg_tmp{i}.iProjAngles = length(ind);
|
||||
end
|
||||
cfg = cfg_tmp; vectors = vectors_tmp;
|
||||
clear projData_tmp
|
||||
else
|
||||
Nproj_groups = 1;
|
||||
cfg = {cfg};
|
||||
vectors = {vectors};
|
||||
end
|
||||
|
||||
if ~isempty(r.deformation_fields) && any(split(1:3) > 1)
|
||||
error('Deformation field splitting not implemented')
|
||||
end
|
||||
|
||||
if gpu.AvailableMemory > 4*(numel(projData)+ ...
|
||||
cfg{1}.iProjU* cfg{1}.iProjV*cfg{1}.iProjAngles * (Nproj_groups>1) ...
|
||||
+any(split>1)*prod(Npix_small)*(2 + ~isa(vol_full, 'gpuArray'))) && ...
|
||||
numel(projData) < intmax('int32')
|
||||
projData = gpuArray(projData); % move small blocks directly on GPU
|
||||
end
|
||||
|
||||
inParpool = ~isempty(getCurrentTask());
|
||||
iter = 1;
|
||||
for k = 1:Nproj_groups
|
||||
if Nproj_groups > 1
|
||||
if length(proj_ind{k}) > 1 && ~inParpool && ~isa(projData, 'gpuArray') && exist('+tomo/get_from_array.m', 'file')
|
||||
% use custom made MEX function from +tomo package, usually
|
||||
% faster but use a lot of CPU
|
||||
projData_small = tomo.get_from_array(projData, [], proj_ind{k}); % load subblock from projections
|
||||
else
|
||||
projData_small = projData(:,:,proj_ind{k}); % split the projections if needed
|
||||
end
|
||||
else
|
||||
projData_small = projData;
|
||||
end
|
||||
if gpu.AvailableMemory < 2*4*numel(projData_small)
|
||||
% in case of low GPU memory, transpose data in RAM
|
||||
projData_small = gpuArray(matlab2astra(projData_small)); % transfer projections to GPU if not there yet
|
||||
else
|
||||
% move subblocks of the data on GPU
|
||||
projData_small = gpuArray(projData_small);
|
||||
projData_small = matlab2astra(projData_small);
|
||||
end
|
||||
if numel(projData_small) * 4 > 1024e6
|
||||
error('Data exceeded maximal size of texture memory 1024MB, Increase "split" to reduce the projection size')
|
||||
|
||||
end
|
||||
|
||||
if gpu.AvailableMemory < prod(Npix_small)*4 && prod(split(1:3)) ~= 1
|
||||
% memory needed to make CUDA array for texture memory
|
||||
pause(0.1)
|
||||
!nvidia-smi
|
||||
whos
|
||||
error('Too low GPU memory, avail: %3.2gGB / req: %3.2gGB, GPU %i/%i', gpu.AvailableMemory/1e9,prod(Npix_small)*4/1e9, gpu.Index, gpuDeviceCount)
|
||||
end
|
||||
for z = 1:split(3)
|
||||
for x = 1:split(1)
|
||||
for y =1:split(2)
|
||||
if r.verbose > 0
|
||||
progressbar(iter, prod(split(1:3))*Nproj_groups+1, 20);
|
||||
end
|
||||
pos = [x,y,z];
|
||||
for n = 1:3
|
||||
% find optimal shift of the subvolume
|
||||
if mod(split(n),2)==1 %% odd
|
||||
shift(n) = (pos(n) - ceil(split(n)/2))*Npix_small(n);
|
||||
else
|
||||
shift(n) = (pos(n) - ceil(split(n)/2)-1/2)*Npix_small(n);
|
||||
end
|
||||
end
|
||||
|
||||
vectors_tmp = vectors{k};
|
||||
vectors_tmp(:,4:6) = bsxfun(@minus, vectors_tmp(:,4:6), shift);
|
||||
if prod(split(1:3))== 1
|
||||
req_mem = numel(projData_small)*4 ;
|
||||
else
|
||||
req_mem = 2*numel(projData_small)*4 ;
|
||||
end
|
||||
if gpu.AvailableMemory < req_mem
|
||||
error('Too low GPU memory, avail: %3.2gGB / req: %3.2gGB, GPU %i/%i', gpu.AvailableMemory/1e9,req_mem/1e9, gpu.Index, gpuDeviceCount)
|
||||
end
|
||||
try
|
||||
if prod(split(1:3))== 1
|
||||
% no splitting, vol_small == vol_full -> write
|
||||
% the backprojection directly to vol_full
|
||||
% without copying -> no output arguments are needed
|
||||
astra.ASTRA_GPU_wrapper('bp', projData_small, cfg{k}, vectors_tmp,vol_full,r.deformation_fields{:});
|
||||
else
|
||||
vol_small = astra.ASTRA_GPU_wrapper('bp', projData_small, cfg{k}, vectors_tmp,[],r.deformation_fields{:});
|
||||
% if some volume split is needed, add the subvolume
|
||||
% to the full volume
|
||||
if ~keep_volume_on_GPU
|
||||
vol_small = gather(vol_small); % return to RAM
|
||||
end
|
||||
if keep_volume_on_GPU || isa(vol_full, 'gpuArray') || ~exist('+tomo/add_to_3D_volume.m', 'file')
|
||||
% return results to the large array
|
||||
% use matlab to do the copying on GPU
|
||||
vol_full =add_to_3D(vol_full, vol_small,([x,y,z]-1).*Npix_small');
|
||||
else
|
||||
% otherwise use paralelized CPU code
|
||||
tomo.add_to_3D_volume(vol_full,vol_small, ([x,y,z]-1).*Npix_small', true);
|
||||
end
|
||||
end
|
||||
catch err
|
||||
if strcmpi(err.identifier,'parallel:gpu:array:OOM')
|
||||
warning('Out of memory on GPU %i, try reset GPU or split the array onto smaller blocks', gpu.Index)
|
||||
gpuDevice
|
||||
end
|
||||
reset(gpuDevice)
|
||||
rethrow(err)
|
||||
end
|
||||
|
||||
iter = iter+1;
|
||||
if r.verbose > 0
|
||||
progressbar(iter, prod(split(1:3))*Nproj_groups+1, 20);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ~r.keep_on_GPU
|
||||
vol_full = gather(vol_full);
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
@@ -0,0 +1,385 @@
|
||||
% AX_PARTIAL forward projector that allows to split the full volume into smaller pieces
|
||||
% composed tomography projector based on ASTRA toolbox
|
||||
% can be used either for data in RAM or on GPU (automatically decided from class of volData)
|
||||
% * volume is split based on "split" parameter, 1 == no splitting
|
||||
% * Ax_partial tries to split data if GPU limits are exceeded (ie texture memory limits)
|
||||
%
|
||||
% projData = Ax_partial(volData, cfg, vectors,split, varargin)
|
||||
%
|
||||
% Inputs:
|
||||
% **volData - array Nx x Ny x Nz of projected volume
|
||||
% **cfg - config structure generated by ASTRA_initialize
|
||||
% **vectors - orientation of projections generated by ASTRA_initialize
|
||||
% **split - 3 or 4 elements vector, [split X, split Y, split Z, split angle ]
|
||||
% *optional*
|
||||
% **deformation_fields - 3x1 cell contaning 3D arrays of local deformation of the object
|
||||
% **GPU - GPU id to be used for reconstruction
|
||||
% **verbose - verbose = 0 : (default) quiet, verbose = 1: standard info , verbose = 2: debug
|
||||
% **keep_on_GPU - if true keep reconstructed volume on GPU to make is faster, default == false (the safe option)
|
||||
%
|
||||
% *returns*
|
||||
% ++projData - projection of volData
|
||||
%
|
||||
% recompile commands
|
||||
% (Linux, GCC 4.8.5) mexcuda -outdir private ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper/util3d.cu ASTRA_GPU_wrapper/par3d_fp.cu ASTRA_GPU_wrapper/par3d_bp.cu
|
||||
% (Windows) mexcuda -outdir private ASTRA_GPU_wrapper\ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper\util3d.cu ASTRA_GPU_wrapper\par3d_fp.cu ASTRA_GPU_wrapper\par3d_bp.cu
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
|
||||
function projData = Ax_partial(volData, cfg, vectors,split, varargin)
|
||||
|
||||
|
||||
import utils.*
|
||||
import math.*
|
||||
|
||||
par = inputParser;
|
||||
par.KeepUnmatched = true;
|
||||
|
||||
par.addOptional('deformation_fields', {}) % deformation_fields: 3x1 cell contaning 3D arrays of local deformation of the object
|
||||
par.addOptional('GPU', []) % GPUs id to be used in reconstruction
|
||||
par.addOptional('verbose', 0) % verbose = 0 : quiet, verbose : standard info , verbose = 2: debug
|
||||
par.addOptional('keep_on_GPU', false) % true - keep reconstructed volume on GPU to make is faster
|
||||
|
||||
par.parse(varargin{:})
|
||||
r = par.Results;
|
||||
|
||||
if isempty(r.deformation_fields); r.deformation_fields = {}; end
|
||||
|
||||
%% check the inputs + check memory availibility on GPU
|
||||
assert(gpuDeviceCount>0, 'No CUDA enabled GPU availible')
|
||||
|
||||
if ~( (isa(volData, 'gpuArray') && strcmp(classUnderlying(volData), 'single')) || ...
|
||||
isa(volData, 'single') ) || ~isreal(volData)
|
||||
error('Only single precision real input array supported')
|
||||
end
|
||||
if ~isempty(r.deformation_fields)
|
||||
assert(any(numel(r.deformation_fields) == [3,6]), 'Deformation field expected as 3x1 or 6x1 cell array')
|
||||
for i = 1:numel( r.deformation_fields)
|
||||
if ~( (isa(r.deformation_fields{i}, 'gpuArray') && strcmp(classUnderlying(r.deformation_fields{i}), 'single')) || ...
|
||||
isa(r.deformation_fields{i}, 'single'))
|
||||
error('Only single precision for deformation fields is supported')
|
||||
end
|
||||
r.deformation_fields{i} = gpuArray(r.deformation_fields{i}); % move on GPU, they are usually small
|
||||
end
|
||||
r.deformation_fields = r.deformation_fields' ; % transpose to that array(:) results in sorted field
|
||||
end
|
||||
|
||||
|
||||
gpu = gpuDevice();
|
||||
if ~isempty(r.GPU) && gpu.Index ~= r.GPU(1)
|
||||
% switch and !! reset !! GPU
|
||||
if isa(volData, 'gpuArray'), error('Switching GPUs will reset content'); end
|
||||
gpu = gpuDevice(r.GPU(1));
|
||||
end
|
||||
|
||||
|
||||
split = ceil(max(1,split));
|
||||
if ismatrix(volData)
|
||||
split = [split([1, min(2,end)]),1];
|
||||
assert(all(size(volData)==[cfg.iVolX,cfg.iVolY]), 'Wrong inputs size')
|
||||
else
|
||||
assert(all(size(volData)==[cfg.iVolX,cfg.iVolY,cfg.iVolZ]), 'Wrong inputs size')
|
||||
end
|
||||
|
||||
keep_on_GPU = isa(volData, 'gpuArray') || r.keep_on_GPU;
|
||||
|
||||
%% backprojector that allows to split the full volume into smaller pieces
|
||||
assert(all(size(vectors,2)==12), 'Wrong "vectors" size')
|
||||
assert(~isempty(vectors), 'Empty input "vectors"')
|
||||
assert(cfg.iVolX*cfg.iVolY*cfg.iVolZ > 0, 'Inputs volume is empty');
|
||||
assert(cfg.iProjU*cfg.iProjV*cfg.iProjAngles > 0, 'Projections are empty');
|
||||
|
||||
% be sure that ASTRA wrapper is feeded by doubles !!
|
||||
for i = fieldnames(cfg)'
|
||||
cfg.(i{1}) = double(cfg.(i{1}));
|
||||
end
|
||||
vectors = double(vectors);
|
||||
split=double(split);
|
||||
|
||||
|
||||
cfg.iProjAngles = size(vectors,1);
|
||||
|
||||
assert(cfg.iProjAngles > 1, 'Number of processed angles must be > 1')
|
||||
|
||||
|
||||
% fix if the number of projections is > 1024, or arrays are too large
|
||||
Nproj_groups = max(1,ceil(max([cfg.iProjAngles/1024, ... % ASTRA constant memory limit
|
||||
cfg.iProjU*cfg.iProjV*cfg.iProjAngles*4 / gpu.AvailableMemory, ... % availible memory limit
|
||||
cfg.iProjU * cfg.iProjV * cfg.iProjAngles / double(intmax('int32'))]))); % maximal array size allowed by CUDA limit
|
||||
if length(split) > 3
|
||||
Nproj_groups = max(split(4), Nproj_groups);
|
||||
end
|
||||
|
||||
if all(split == 1) && Nproj_groups == 1
|
||||
if numel(volData)*4 > 1024e6 % exceeded texture memory
|
||||
nsubVol = ceil(numel(volData)*4 / 1024e6);
|
||||
nsubVol = 2^nextpow2(nsubVol);
|
||||
% split = ceil([sqrt(nsubVol),sqrt(nsubVol),1]);
|
||||
split = [1,1,nsubVol];
|
||||
if r.verbose>0; disp(['Volume array is larger than 1024MB, auto-splitting ', num2str(split)]); end
|
||||
else
|
||||
%% in the simple case call ASTRA_GPU_wrapper directly
|
||||
volData = gpuArray(volData);
|
||||
% call ASTRA
|
||||
projData = astra.ASTRA_GPU_wrapper('fp',volData, cfg, vectors,[],r.deformation_fields{:});
|
||||
clear volData
|
||||
if gpu.AvailableMemory < 4*numel(projData)
|
||||
projData = gather(projData); % prevent out of memory errors during next step
|
||||
end
|
||||
projData = astra2matlab(projData);
|
||||
if ~keep_on_GPU; projData = gather(projData); end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
%% otherwise prepare data for split and call ASTRA_GPU_wrapper on subvolumes
|
||||
|
||||
|
||||
if isscalar(split)
|
||||
split = split .* ones(ndims(volData),1);
|
||||
end
|
||||
assert(numel(volData)*4/prod(split) <= 1024e6, 'Volume array exceeded 1024MB, use more splitting')
|
||||
|
||||
Nvol_full = size(volData);
|
||||
if numel(Nvol_full)<3
|
||||
Nvol_full(3)=1;
|
||||
end
|
||||
Nvol_sub = Nvol_full'./reshape(split(1:3),[],1);
|
||||
|
||||
assert(all(mod(Nvol_sub,1)==0), sprintf('Volume size %ix%ix%i is not dividable by split %ix%ix%i',size(volData),split(1:3)))
|
||||
|
||||
|
||||
if ismatrix(volData)
|
||||
split(3) = 1;
|
||||
Nvol_sub(3) = 1;
|
||||
end
|
||||
|
||||
cfg.iVolX = cfg.iVolX/split(1);
|
||||
cfg.iVolY = cfg.iVolY/split(2);
|
||||
cfg.iVolZ = cfg.iVolZ/split(3);
|
||||
|
||||
cfg_orig = cfg;
|
||||
|
||||
if Nproj_groups > 1
|
||||
% split the projections along the angles
|
||||
for i = 1:Nproj_groups
|
||||
ind = (1+(i-1)*ceil(cfg.iProjAngles/Nproj_groups)):i*ceil(cfg.iProjAngles/Nproj_groups);
|
||||
ind = ind(ind <= cfg.iProjAngles);
|
||||
vectors_tmp{i} = vectors(ind,:);
|
||||
cfg_tmp{i} = cfg;
|
||||
cfg_tmp{i}.iProjAngles = length(ind);
|
||||
end
|
||||
cfg = cfg_tmp; vectors = vectors_tmp;
|
||||
else
|
||||
Nproj_groups = 1;
|
||||
cfg = {cfg};
|
||||
vectors = {vectors};
|
||||
end
|
||||
clear ind
|
||||
|
||||
|
||||
if r.verbose > 1
|
||||
fprintf('Size of the full volume: %i %i %i\n', size(volData))
|
||||
fprintf('Size of the subvolume: %i %i %i\n', Nvol_sub)
|
||||
fprintf('Size of the one sinogram block: %i %i %i\n', cfg{1}.iProjU, cfg{1}.iProjV, cfg{1}.iProjAngles)
|
||||
end
|
||||
|
||||
%%!!!! note that in rare cases astra my fail if sinogram width is too small
|
||||
|
||||
assert(prod(Nvol_sub) * 4 <= 1024e6, 'Volume exceeded maximal size of texture 1024MB')
|
||||
|
||||
% estimate required memory + (use only if 2x more memory is available)
|
||||
required_mem = 2*(prod(Nvol_sub)*(2+~isa(volData, 'gpuArray')) + cfg_orig.iProjU * cfg_orig.iProjV * cfg_orig.iProjAngles )*4;
|
||||
% keep projections on GPU only of there is enough memory
|
||||
keep_projections_on_GPU = Nproj_groups == 1 || gpu.AvailableMemory > required_mem;
|
||||
|
||||
if cfg{1}.iProjU * cfg{1}.iProjV * cfg{1}.iProjAngles > intmax('int32')
|
||||
error('Projection size exceeded maximum size allowed on GPU')
|
||||
end
|
||||
if (keep_on_GPU || prod(split(1:3)) == 1) && numel(volData) < intmax('int32') % keep volume on GPU
|
||||
volData = gpuArray(volData);
|
||||
end
|
||||
|
||||
inParpool = ~isempty(getCurrentTask());
|
||||
iter = 1;
|
||||
for m = 1:Nproj_groups
|
||||
% split angularly (solve smaller groups of angles)
|
||||
% allocate memory for the projections
|
||||
projData{m} = gpuArray.zeros(cfg{m}.iProjU, cfg{m}.iProjV, cfg{m}.iProjAngles, 'single');
|
||||
% split into volume cubes
|
||||
for i = 1:split(1)
|
||||
for j = 1:split(2)
|
||||
for k = 1:split(3)
|
||||
if r.verbose > 0
|
||||
progressbar(iter, prod(split(1:3))*Nproj_groups+1, 20);
|
||||
end
|
||||
pos = [i,j,k];
|
||||
for n = 1:3
|
||||
ind{n} = (1+(pos(n)-1)*Nvol_sub(n)):(pos(n)*Nvol_sub(n));
|
||||
%% find optimal shift of the subvolume
|
||||
if mod(split(n),2)==1 %% odd
|
||||
shift(n) = (pos(n) - ceil(split(n)/2))*Nvol_sub(n);
|
||||
else
|
||||
shift(n) = (pos(n) - ceil(split(n)/2)-1/2)*Nvol_sub(n);
|
||||
end
|
||||
end
|
||||
|
||||
% extract subvolume to be processed
|
||||
if any(split(1:3)~=1) && isa(volData, 'gpuArray')
|
||||
vol_small = volData(ind{:}); % take only small subvolume
|
||||
elseif any(split(1:3)~=1)
|
||||
vol_small = zeros(Nvol_sub','single');
|
||||
utils.get_from_3D_projection(vol_small,volData,[ind{1}(1),ind{2}(1)]-1,ind{3});
|
||||
else
|
||||
vol_small = volData; % avoid data copying of possible
|
||||
end
|
||||
vol_small = gpuArray(vol_small);
|
||||
|
||||
% split deformation field for nonrigid tomography
|
||||
if ~isempty(r.deformation_fields)
|
||||
for ii = 1:3
|
||||
N_deform = size(r.deformation_fields{ii}) ./ reshape(split(1:3),[],1)';
|
||||
for jj = 1:3
|
||||
ind_def{jj} = linspace(1+(pos(jj)-1)*N_deform(jj), pos(jj)*N_deform(jj), size(r.deformation_fields{ii},jj));
|
||||
end
|
||||
[X,Y,Z]= meshgrid(ind_def{:});
|
||||
deformation_fields_sub{ii} = interp3(r.deformation_fields{ii},X,Y,Z);
|
||||
end
|
||||
else
|
||||
deformation_fields_sub = {};
|
||||
end
|
||||
|
||||
vec = vectors{m};
|
||||
vec(:,4:6) = bsxfun(@minus, vec(:,4:6), shift);
|
||||
|
||||
req_mem = 2*numel(vol_small)*4 ;
|
||||
if gpu.AvailableMemory < req_mem
|
||||
!nvidia-smi
|
||||
whos
|
||||
error('Too low GPU memory, avail: %3.2gGB / req: %3.2gGB, GPU %i/%i, projection group %i/%i, keep_proj_on_GPU=%i', gpu.AvailableMemory/1e9,req_mem/1e9, gpu.Index, gpuDeviceCount, m , Nproj_groups, keep_projections_on_GPU)
|
||||
end
|
||||
|
||||
try
|
||||
% avoid memory allocation, write directly to projData{m} -> no output arguments are needed
|
||||
astra.ASTRA_GPU_wrapper('fp',vol_small, cfg{m}, vec,projData{m}, deformation_fields_sub{:});
|
||||
vol_small = []; % soft mem clean
|
||||
catch err
|
||||
if strcmpi(err.identifier,'parallel:gpu:array:OOM')
|
||||
warning('Out of memory on GPU %i, try reset GPU or split the array onto smaller blocks', gpu.Index)
|
||||
gpuDevice
|
||||
reset(gpuDevice)
|
||||
end
|
||||
|
||||
rethrow(err)
|
||||
end
|
||||
|
||||
iter = iter+1;
|
||||
if r.verbose>0
|
||||
progressbar(iter, prod(split(1:3))*Nproj_groups+1, 20);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if ~keep_projections_on_GPU
|
||||
projData{m} = gather(projData{m});
|
||||
end
|
||||
end
|
||||
clear volData vol_small
|
||||
|
||||
|
||||
% permute / concatenate
|
||||
if gpu.AvailableMemory < 4*numel(projData{1})*max(2,Nproj_groups)
|
||||
projData = gather_all(projData);
|
||||
end
|
||||
|
||||
projData = astra2matlab(projData);
|
||||
|
||||
if gpu.AvailableMemory < 8*numel(projData{1})*Nproj_groups
|
||||
projData = gather_all(projData);
|
||||
end
|
||||
% concatenate the projected data align the angular (3rd) axis
|
||||
projData = merge_projections(projData);
|
||||
|
||||
if ~keep_on_GPU
|
||||
projData = gather(projData);
|
||||
elseif numel(projData) < intmax('int32') && gpu.AvailableMemory < 4*numel(projData)
|
||||
% return to GPU if requested by 'keep_on_GPU' parameter
|
||||
projData = gpuArray(projData);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function x = gather_all(x)
|
||||
for i = 1:length(x)
|
||||
x{i} = gather(x{i});
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function projData = merge_projections(projData_blocks)
|
||||
Nblocks = length(projData_blocks);
|
||||
if Nblocks > 1
|
||||
if isa(projData_blocks{1}, 'gpuArray')
|
||||
projData = cat(3, projData_blocks{:});
|
||||
else
|
||||
% faster and more memory efficient version
|
||||
proj_size = [size(projData_blocks{1},1),size(projData_blocks{1},2),sum(cellfun(@(x)size(x,3), projData_blocks))];
|
||||
for ii = 1:10
|
||||
try
|
||||
projData = zeros(proj_size, 'single');
|
||||
break
|
||||
catch err
|
||||
end
|
||||
pause(1)
|
||||
end
|
||||
if ii == 10
|
||||
warning('Unsufficient memory to allocate %3.2gGB RAM', prod(proj_size)*4/1e9)
|
||||
utils.check_available_memory
|
||||
rethrow(err)
|
||||
end
|
||||
offset = 0;
|
||||
for ii = 1:Nblocks
|
||||
tomo.set_to_array(projData, projData_blocks{ii}, offset);
|
||||
offset = offset + size(projData_blocks{ii},3);
|
||||
end
|
||||
end
|
||||
else
|
||||
projData = projData_blocks{1};
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% example script for ASTRA wrappers
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
%
|
||||
% recompile commands
|
||||
% (Linux, GCC 4.8.5) mexcuda -outdir private ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper/util3d.cu ASTRA_GPU_wrapper/par3d_fp.cu ASTRA_GPU_wrapper/par3d_bp.cu
|
||||
% (Windows) mexcuda -outdir private ASTRA_GPU_wrapper\ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper\util3d.cu ASTRA_GPU_wrapper\par3d_fp.cu ASTRA_GPU_wrapper\par3d_bp.cu
|
||||
|
||||
|
||||
% volume settings
|
||||
Npix_vol = [300, 300, 100] ;
|
||||
Nangles = 400;
|
||||
Npix_proj = [400, 400];
|
||||
|
||||
% create "data"
|
||||
angles = linspace(0, 360, Nangles);
|
||||
lamino_angle = 60;
|
||||
volData = ones(Npix_vol, 'single');
|
||||
|
||||
% generate geometry
|
||||
[cfg, vectors] = astra.ASTRA_initialize(Npix_vol, Npix_proj, angles, lamino_angle);
|
||||
% find optimal split, for small volumes below 600^3 no split is needed
|
||||
split = astra.ASTRA_find_optimal_split(cfg);
|
||||
|
||||
% generate projections
|
||||
projData = astra.Ax_partial(volData, cfg, vectors, split);
|
||||
|
||||
figure(1)
|
||||
% plot the projections
|
||||
subplot(1,2,1)
|
||||
plotting.imagesc3D(projData); axis off image; colormap bone
|
||||
title('Angular geometry')
|
||||
|
||||
% do backprojection projections !!! not FBP !!!
|
||||
backprojData = astra.Atx_partial(projData, cfg, vectors, split);
|
||||
|
||||
|
||||
% generate rotation matrix, note that the expected angles needs to be
|
||||
% adjusted to provide same results are the previous example
|
||||
R3 = utils.get_rotation_matrix_3D((90-lamino_angle)*ones(Nangles,1), -angles, zeros(Nangles,1));
|
||||
|
||||
% generate geometry
|
||||
[cfgR, vectorsR] = astra.ASTRA_initialize(Npix_vol, Npix_proj, R3);
|
||||
|
||||
% generate projections
|
||||
projDataR = astra.Ax_partial(volData, cfgR, vectorsR, split);
|
||||
|
||||
figure(1)
|
||||
% plot the projections
|
||||
subplot(1,2,2)
|
||||
% plot the projections
|
||||
plotting.imagesc3D(projDataR); axis off image; colormap bone
|
||||
title('Rotation matrix geometry')
|
||||
|
||||
|
||||
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
@@ -0,0 +1,54 @@
|
||||
% IRADON_GPU_WRAPPER back projector that allows to split the full volume
|
||||
% into smaller pieces, tomography projector is based on ASTRA toolbox can be used
|
||||
% either for data in RAM or on GPU (automatically decided from class of volData)
|
||||
%
|
||||
% vol = iradon_gpu_wrapper(sinogram, cfg, vectors)
|
||||
%
|
||||
% Inputs:
|
||||
% **projData - array Nlayers x width_sinogram x Nangles of back-projected data
|
||||
% **cfg - config structure generated by ASTRA_initialize
|
||||
% **vectors - orientation of projections generated by ASTRA_initialize
|
||||
% Outputs:
|
||||
% ++vol - backprojected volume
|
||||
%
|
||||
% recompile commands
|
||||
% (Linux) mexcuda -largeArrayDims -outdir private ASTRA_GPU_wrapper/ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper/util3d.cu ASTRA_GPU_wrapper/par3d_fp.cu ASTRA_GPU_wrapper/par3d_bp.cu
|
||||
% (Windows) mexcuda -largeArrayDims -outdir private ASTRA_GPU_wrapper\ASTRA_GPU_wrapper.cu ASTRA_GPU_wrapper\util3d.cu ASTRA_GPU_wrapper\par3d_fp.cu ASTRA_GPU_wrapper\par3d_bp.cu
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
|
||||
function vol = iradon_gpu_wrapper(sinogram, cfg, vectors)
|
||||
vol = ASTRA_GPU_wrapper('bp', sinogram, cfg, vectors);
|
||||
end
|
||||
@@ -0,0 +1,58 @@
|
||||
% FUNCTION full_array = add_to_3D(full_array, small_array, position)
|
||||
% add one small 3D block into large 3D array
|
||||
% Inputs:
|
||||
% full_array
|
||||
% small_array
|
||||
% position - offset from (1,1,1) coordinate in pixels
|
||||
|
||||
% *-----------------------------------------------------------------------*
|
||||
% | |
|
||||
% | Except where otherwise noted, this work is licensed under a |
|
||||
% | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
% | International (CC BY-NC-SA 4.0) license. |
|
||||
% | |
|
||||
% | Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
% | |
|
||||
% | Author: CXS group, PSI |
|
||||
% *-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
% proper use and the correctness of the results.
|
||||
%
|
||||
%
|
||||
%
|
||||
function full_array = add_to_3D(full_array, small_array, position)
|
||||
|
||||
position = round(position);
|
||||
N_f = size(full_array);
|
||||
N_s = size(small_array);
|
||||
|
||||
for i = 1:ndims(full_array)
|
||||
ind_f{i} = unique(min(N_f(i),max(1,position(i)+(1:N_s(i)))));
|
||||
ind_s{i} = unique(min(N_s(i),max(1,ind_f{i}-position(i))));
|
||||
end
|
||||
|
||||
full_array(ind_f{:}) = full_array(ind_f{:}) + small_array(ind_s{:});
|
||||
|
||||
|
||||
end
|
||||
@@ -0,0 +1,51 @@
|
||||
% FUNCTION sinogram = astra2matlab(sinogram)
|
||||
% simple function to reshape sinogram from ASTRA (C++) order to Matlab (FORTRAN) order
|
||||
% Inputs:
|
||||
% sinogram - real value sinogram
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
%
|
||||
|
||||
function sinogram = astra2matlab(sinogram)
|
||||
% simple function to reshape sinogram from ASTRA (C++) order to Matlab (FORTRAN) order
|
||||
|
||||
if isnumeric(sinogram)
|
||||
[U,V,A] = size(sinogram);
|
||||
sinogram = permute(reshape(sinogram, [U,A,V]), [3,1,2]);
|
||||
elseif iscell(sinogram)
|
||||
for ii = 1:length(sinogram)
|
||||
sinogram{ii} = astra2matlab(sinogram{ii});
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,52 @@
|
||||
% FUNCTION sinogram = astra2matlab(sinogram)
|
||||
% simple function to reshape sinogram from Matlab (FORTRAN) order to ASTRA (C++) order
|
||||
% Inputs:
|
||||
% sinogram - real value sinogram
|
||||
|
||||
%*-----------------------------------------------------------------------*
|
||||
%| |
|
||||
%| Except where otherwise noted, this work is licensed under a |
|
||||
%| Creative Commons Attribution-NonCommercial-ShareAlike 4.0 |
|
||||
%| International (CC BY-NC-SA 4.0) license. |
|
||||
%| |
|
||||
%| Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) |
|
||||
%| |
|
||||
%| Author: CXS group, PSI |
|
||||
%*-----------------------------------------------------------------------*
|
||||
% You may use this code with the following provisions:
|
||||
%
|
||||
% If the code is fully or partially redistributed, or rewritten in another
|
||||
% computing language this notice should be included in the redistribution.
|
||||
%
|
||||
% If this code, or subfunctions or parts of it, is used for research in a
|
||||
% publication or if it is fully or partially rewritten for another
|
||||
% computing language the authors and institution should be acknowledged
|
||||
% in written form in the publication: “Data processing was carried out
|
||||
% using the “cSAXS matlab package” developed by the CXS group,
|
||||
% Paul Scherrer Institut, Switzerland.”
|
||||
% Variations on the latter text can be incorporated upon discussion with
|
||||
% the CXS group if needed to more specifically reflect the use of the package
|
||||
% for the published work.
|
||||
%
|
||||
% A publication that focuses on describing features, or parameters, that
|
||||
% are already existing in the code should be first discussed with the
|
||||
% authors.
|
||||
%
|
||||
% This code and subroutines are part of a continuous development, they
|
||||
% are provided “as they are” without guarantees or liability on part
|
||||
% of PSI or the authors. It is the user responsibility to ensure its
|
||||
%
|
||||
|
||||
|
||||
function sinogram = matlab2astra(sinogram)
|
||||
|
||||
if isnumeric(sinogram)
|
||||
[U,V,A] = size(sinogram);
|
||||
sinogram = reshape(permute(sinogram, [2,3,1]),[U,V,A]);
|
||||
elseif iscell(sinogram)
|
||||
for ii = 1:length(sinogram)
|
||||
sinogram{ii} = matlab2astra(sinogram{ii});
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user