/*
-----------------------------------------------------------------------
Copyright: 2010-2021, imec Vision Lab, University of Antwerp
2014-2021, CWI, Amsterdam
Contact: astra@astra-toolbox.com
Website: http://www.astra-toolbox.com/
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 .
-----------------------------------------------------------------------
*/
/** \file astra_mex_direct_c.cpp
*
* \brief Utility functions for low-overhead FP and BP calls.
*/
#include
#include "mexHelpFunctions.h"
#include "mexCopyDataHelpFunctions.h"
#include "mexDataManagerHelpFunctions.h"
#include
#include "astra/Globals.h"
#include "astra/AstraObjectManager.h"
#include "astra/Float32ProjectionData2D.h"
#include "astra/Float32VolumeData2D.h"
#include "astra/CudaProjector3D.h"
#include "astra/Projector3D.h"
#include "astra/Float32ProjectionData3DMemory.h"
#include "astra/Float32VolumeData3DMemory.h"
#include "astra/CudaForwardProjectionAlgorithm3D.h"
#include "astra/CudaBackProjectionAlgorithm3D.h"
using namespace std;
using namespace astra;
class CFloat32CustomMemory_simple : public astra::CFloat32CustomMemory {
public:
CFloat32CustomMemory_simple(float *ptr) { m_fPtr = ptr; }
~CFloat32CustomMemory_simple() { }
};
#ifdef ASTRA_CUDA
//-----------------------------------------------------------------------------------------
/**
* projection = astra_mex_direct_c('FP3D', projector_id, volume);
* Both 'projection' and 'volume' are Matlab arrays.
*/
void astra_mex_direct_fp3d(int& nlhs, mxArray* plhs[], int& nrhs, const mxArray* prhs[])
{
// TODO: Add an optional way of specifying extra options
if (nrhs < 3) {
mexErrMsgTxt("Not enough arguments. Syntax: astra_mex_direct_c('FP3D', projector_id, data)");
return;
}
int iPid = (int)(mxGetScalar(prhs[1]));
astra::CProjector3D* pProjector;
pProjector = astra::CProjector3DManager::getSingleton().get(iPid);
if (!pProjector) {
mexErrMsgTxt("Projector not found.");
return;
}
if (!pProjector->isInitialized()) {
mexErrMsgTxt("Projector not initialized.");
return;
}
bool isCuda = false;
if (dynamic_cast(pProjector))
isCuda = true;
if (!isCuda) {
mexErrMsgTxt("Only CUDA projectors are currently supported.");
return;
}
astra::CVolumeGeometry3D* pVolGeom = pProjector->getVolumeGeometry();
astra::CProjectionGeometry3D* pProjGeom = pProjector->getProjectionGeometry();
const mxArray* const data = prhs[2];
if (!checkDataType(data)) {
mexErrMsgTxt("Data must be single or double.");
return;
}
if (!checkDataSize(data, pVolGeom)) {
mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry.");
return;
}
// Allocate input data
astra::CFloat32VolumeData3DMemory* pInput;
if (mxIsSingle(data)) {
astra::CFloat32CustomMemory* m = new CFloat32CustomMemory_simple((float *)mxGetData(data));
pInput = new astra::CFloat32VolumeData3DMemory(pVolGeom, m);
} else {
pInput = new astra::CFloat32VolumeData3DMemory(pVolGeom);
copyMexToCFloat32Array(data, pInput->getData(), pInput->getSize());
}
// Allocate output data
// If the input is single, we also allocate single output.
// Otherwise, double.
astra::CFloat32ProjectionData3DMemory* pOutput;
mxArray *pOutputMx;
if (mxIsSingle(data)) {
mwSize dims[3];
dims[0] = pProjGeom->getDetectorColCount();
dims[1] = pProjGeom->getProjectionCount();
dims[2] = pProjGeom->getDetectorRowCount();
// Allocate uninitialized mxArray of size dims.
// (It will be zeroed by CudaForwardProjectionAlgorithm3D)
const mwSize zero_dims[2] = {0, 0};
pOutputMx = mxCreateNumericArray(2, zero_dims, mxSINGLE_CLASS, mxREAL);
mxSetDimensions(pOutputMx, dims, 3);
const mwSize num_elems = mxGetNumberOfElements(pOutputMx);
const mwSize elem_size = mxGetElementSize(pOutputMx);
mxSetData(pOutputMx, mxMalloc(elem_size * num_elems));
astra::CFloat32CustomMemory* m = new CFloat32CustomMemory_simple((float *)mxGetData(pOutputMx));
pOutput = new astra::CFloat32ProjectionData3DMemory(pProjGeom, m);
} else {
pOutput = new astra::CFloat32ProjectionData3DMemory(pProjGeom);
}
// Perform FP
astra::CCudaForwardProjectionAlgorithm3D* pAlg;
pAlg = new astra::CCudaForwardProjectionAlgorithm3D();
pAlg->initialize(pProjector, pOutput, pInput);
if (!pAlg->isInitialized()) {
mexErrMsgTxt("Error initializing algorithm.");
// TODO: Delete pOutputMx?
delete pAlg;
delete pInput;
delete pOutput;
return;
}
pAlg->run();
delete pAlg;
if (mxIsSingle(data)) {
} else {
pOutputMx = createEquivMexArray(pOutput);
copyCFloat32ArrayToMex(pOutput->getData(), pOutputMx);
}
plhs[0] = pOutputMx;
delete pOutput;
delete pInput;
}
//-----------------------------------------------------------------------------------------
/**
* projection = astra_mex_direct_c('BP3D', projector_id, volume);
* Both 'projection' and 'volume' are Matlab arrays.
*/
void astra_mex_direct_bp3d(int& nlhs, mxArray* plhs[], int& nrhs, const mxArray* prhs[])
{
// TODO: Add an optional way of specifying extra options
if (nrhs < 3) {
mexErrMsgTxt("Not enough arguments. Syntax: astra_mex_direct_c('BP3D', projector_id, data)");
return;
}
int iPid = (int)(mxGetScalar(prhs[1]));
astra::CProjector3D* pProjector;
pProjector = astra::CProjector3DManager::getSingleton().get(iPid);
if (!pProjector) {
mexErrMsgTxt("Projector not found.");
return;
}
if (!pProjector->isInitialized()) {
mexErrMsgTxt("Projector not initialized.");
return;
}
bool isCuda = false;
if (dynamic_cast(pProjector))
isCuda = true;
if (!isCuda) {
mexErrMsgTxt("Only CUDA projectors are currently supported.");
return;
}
astra::CVolumeGeometry3D* pVolGeom = pProjector->getVolumeGeometry();
astra::CProjectionGeometry3D* pProjGeom = pProjector->getProjectionGeometry();
const mxArray* const data = prhs[2];
if (!checkDataType(data)) {
mexErrMsgTxt("Data must be single or double.");
return;
}
if (!checkDataSize(data, pProjGeom)) {
mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry.");
return;
}
// Allocate input data
astra::CFloat32ProjectionData3DMemory* pInput;
if (mxIsSingle(data)) {
astra::CFloat32CustomMemory* m = new CFloat32CustomMemory_simple((float *)mxGetData(data));
pInput = new astra::CFloat32ProjectionData3DMemory(pProjGeom, m);
} else {
pInput = new astra::CFloat32ProjectionData3DMemory(pProjGeom);
copyMexToCFloat32Array(data, pInput->getData(), pInput->getSize());
}
// Allocate output data
// If the input is single, we also allocate single output.
// Otherwise, double.
astra::CFloat32VolumeData3DMemory* pOutput;
mxArray *pOutputMx;
if (mxIsSingle(data)) {
mwSize dims[3];
dims[0] = pVolGeom->getGridColCount();
dims[1] = pVolGeom->getGridRowCount();
dims[2] = pVolGeom->getGridSliceCount();
// Allocate uninitialized mxArray of size dims.
// (It will be zeroed by CudaBackProjectionAlgorithm3D)
const mwSize zero_dims[2] = {0, 0};
pOutputMx = mxCreateNumericArray(2, zero_dims, mxSINGLE_CLASS, mxREAL);
mxSetDimensions(pOutputMx, dims, 3);
const mwSize num_elems = mxGetNumberOfElements(pOutputMx);
const mwSize elem_size = mxGetElementSize(pOutputMx);
mxSetData(pOutputMx, mxMalloc(elem_size * num_elems));
astra::CFloat32CustomMemory* m = new CFloat32CustomMemory_simple((float *)mxGetData(pOutputMx));
pOutput = new astra::CFloat32VolumeData3DMemory(pVolGeom, m);
} else {
pOutput = new astra::CFloat32VolumeData3DMemory(pVolGeom);
}
// Perform BP
astra::CCudaBackProjectionAlgorithm3D* pAlg;
pAlg = new astra::CCudaBackProjectionAlgorithm3D();
pAlg->initialize(pProjector, pInput, pOutput);
if (!pAlg->isInitialized()) {
mexErrMsgTxt("Error initializing algorithm.");
// TODO: Delete pOutputMx?
delete pAlg;
delete pInput;
delete pOutput;
return;
}
pAlg->run();
delete pAlg;
if (mxIsSingle(data)) {
} else {
pOutputMx = createEquivMexArray(pOutput);
copyCFloat32ArrayToMex(pOutput->getData(), pOutputMx);
}
plhs[0] = pOutputMx;
delete pOutput;
delete pInput;
}
#endif
//-----------------------------------------------------------------------------------------
static void printHelp()
{
mexPrintf("Please specify a mode of operation.\n");
mexPrintf("Valid modes: FP3D, BP3D\n");
}
//-----------------------------------------------------------------------------------------
/**
* ... = astra_mex_direct_c(mode,...);
*/
void mexFunction(int nlhs, mxArray* plhs[],
int nrhs, const mxArray* prhs[])
{
// INPUT: Mode
string sMode;
if (1 <= nrhs) {
sMode = mexToString(prhs[0]);
} else {
printHelp();
return;
}
#ifndef ASTRA_CUDA
mexErrMsgTxt("Only CUDA projectors are currently supported.");
return;
#else
// 3D data
if (sMode == "FP3D") {
astra_mex_direct_fp3d(nlhs, plhs, nrhs, prhs);
} else if (sMode == "BP3D") {
astra_mex_direct_bp3d(nlhs, plhs, nrhs, prhs);
} else {
printHelp();
}
#endif
return;
}