/* ----------------------------------------------------------------------- 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_data2d_c.cpp * * \brief Creates, manages and manipulates 2D volume and projection data objects. */ #include #include "mexHelpFunctions.h" #include "mexInitFunctions.h" #include #include "astra/Globals.h" #include "astra/AstraObjectManager.h" #include "astra/Float32ProjectionData2D.h" #include "astra/Float32VolumeData2D.h" #include "astra/SparseMatrixProjectionGeometry2D.h" #include "astra/FanFlatProjectionGeometry2D.h" #include "astra/FanFlatVecProjectionGeometry2D.h" #include "astra/ParallelVecProjectionGeometry2D.h" using namespace std; using namespace astra; //----------------------------------------------------------------------------------------- /** astra_mex_data2d('delete', id1, id2, ...); * * Delete one or more data objects currently stored in the astra-library. * id1, id2, ... : identifiers of the 2d data objects as stored in the astra-library. */ void astra_mex_data2d_delete(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: read input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list. \n"); return; } // step2: delete all specified data objects for (int i = 1; i < nrhs; i++) { int iDataID = (int)(mxGetScalar(prhs[i])); CData2DManager::getSingleton().remove(iDataID); } } //----------------------------------------------------------------------------------------- /** astra_mex_data2d('clear'); * * Delete all data objects currently stored in the astra-library. */ void astra_mex_data2d_clear(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { CData2DManager::getSingleton().clear(); } //----------------------------------------------------------------------------------------- /** id = astra_mex_data2d('create', datatype, geometry, data); * * Create a new data 2d object in the astra-library. * type: '-vol' for volume data, '-sino' for projection data * geom: MATLAB struct with the geometry for the data * data: Optional. Can be either a MATLAB matrix containing the data. In that case the dimensions * should match that of the geometry of the object. It can also be a single value, in which case * the entire data will be set to that value. If this isn't specified all values are set to 0. * id: identifier of the 2d data object as it is now stored in the astra-library. */ void astra_mex_data2d_create(int& nlhs, mxArray* plhs[], int& nrhs, const mxArray* prhs[]) { // step1: get datatype if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list. \n"); return; } string sDataType = mexToString(prhs[1]); CFloat32Data2D* pDataObject2D = NULL; if (nrhs >= 4 && !(mexIsScalar(prhs[3])|| mxIsDouble(prhs[3]) || mxIsLogical(prhs[3]) || mxIsSingle(prhs[3]) )) { mexErrMsgTxt("Data must be single, double or logical."); return; } if (mxIsSparse(prhs[2])) { mexErrMsgTxt("Data may not be sparse."); return; } // SWITCH DataType if (sDataType == "-vol") { // Read geometry if (!mxIsStruct(prhs[2])) { mexErrMsgTxt("Argument 3 is not a valid MATLAB struct.\n"); } Config* cfg = structToConfig("VolumeGeometry", prhs[2]); CVolumeGeometry2D* pGeometry = new CVolumeGeometry2D(); if (!pGeometry->initialize(*cfg)) { mexErrMsgTxt("Geometry class not initialized. \n"); delete cfg; delete pGeometry; return; } // If data is specified, check dimensions if (nrhs >= 4 && !mexIsScalar(prhs[3])) { if (pGeometry->getGridColCount() != mxGetN(prhs[3]) || pGeometry->getGridRowCount() != mxGetM(prhs[3])) { mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry. \n"); delete cfg; delete pGeometry; return; } } // Initialize data object pDataObject2D = new CFloat32VolumeData2D(pGeometry); delete pGeometry; delete cfg; } else if (sDataType == "-sino") { // Read geometry if (!mxIsStruct(prhs[2])) { mexErrMsgTxt("Argument 3 is not a valid MATLAB struct.\n"); } Config* cfg = structToConfig("ProjectionGeometry", prhs[2]); // FIXME: Change how the base class is created. (This is duplicated // in 'change_geometry' and Projector2D.cpp.) std::string type = cfg->self.getAttribute("type"); CProjectionGeometry2D* pGeometry; if (type == "sparse_matrix") { pGeometry = new CSparseMatrixProjectionGeometry2D(); } else if (type == "fanflat") { //CFanFlatProjectionGeometry2D* pFanFlatProjectionGeometry = new CFanFlatProjectionGeometry2D(); //pFanFlatProjectionGeometry->initialize(Config(node)); //m_pProjectionGeometry = pFanFlatProjectionGeometry; pGeometry = new CFanFlatProjectionGeometry2D(); } else if (type == "fanflat_vec") { pGeometry = new CFanFlatVecProjectionGeometry2D(); } else if (type == "parallel_vec") { pGeometry = new CParallelVecProjectionGeometry2D(); } else { pGeometry = new CParallelProjectionGeometry2D(); } if (!pGeometry->initialize(*cfg)) { mexErrMsgTxt("Geometry class not initialized. \n"); delete pGeometry; delete cfg; return; } // If data is specified, check dimensions if (nrhs >= 4 && !mexIsScalar(prhs[3])) { if (pGeometry->getDetectorCount() != mxGetN(prhs[3]) || pGeometry->getProjectionAngleCount() != mxGetM(prhs[3])) { mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry. \n"); delete pGeometry; delete cfg; return; } } // Initialize data object pDataObject2D = new CFloat32ProjectionData2D(pGeometry); delete pGeometry; delete cfg; } else { mexErrMsgTxt("Invalid datatype. Please specify '-vol' or '-sino'. \n"); return; } // Check initialization if (!pDataObject2D->isInitialized()) { mexErrMsgTxt("Couldn't initialize data object.\n"); delete pDataObject2D; return; } // Store data if (nrhs == 3) { for (int i = 0; i < pDataObject2D->getSize(); ++i) { pDataObject2D->getData()[i] = 0.0f; } } // Store data if (nrhs >= 4) { // fill with scalar value if (mexIsScalar(prhs[3])) { float32 fValue = (float32)mxGetScalar(prhs[3]); for (int i = 0; i < pDataObject2D->getSize(); ++i) { pDataObject2D->getData()[i] = fValue; } } // fill with array value else { const mwSize* dims = mxGetDimensions(prhs[3]); // Check Data dimensions if (pDataObject2D->getWidth() != mxGetN(prhs[3]) || pDataObject2D->getHeight() != mxGetM(prhs[3])) { mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry. \n"); return; } // logical data if (mxIsLogical(prhs[3])) { mxLogical* pbMatlabData = mxGetLogicals(prhs[3]); int i = 0; int col, row; for (col = 0; col < dims[1]; ++col) { for (row = 0; row < dims[0]; ++row) { pDataObject2D->getData2D()[row][col] = (float32)pbMatlabData[i]; ++i; } } // double data } else if (mxIsDouble(prhs[3])) { double* pdMatlabData = mxGetPr(prhs[3]); int i = 0; int col, row; for (col = 0; col < dims[1]; ++col) { for (row = 0; row < dims[0]; ++row) { pDataObject2D->getData2D()[row][col] = pdMatlabData[i]; ++i; } } // single data } else if (mxIsSingle(prhs[3])) { const float* pfMatlabData = (const float *)mxGetData(prhs[3]); int i = 0; int col, row; for (col = 0; col < dims[1]; ++col) { for (row = 0; row < dims[0]; ++row) { pDataObject2D->getData2D()[row][col] = pfMatlabData[i]; ++i; } } } else { ASTRA_ASSERT(false); } } } // step4: store data object int iIndex = CData2DManager::getSingleton().store(pDataObject2D); // step5: return data id if (1 <= nlhs) { plhs[0] = mxCreateDoubleScalar(iIndex); } } //----------------------------------------------------------------------------------------- /** astra_mex_data2d('store', id, data); * * Store data in an existing astra 2d dataobject with a MATLAB matrix or with a scalar value. * id: identifier of the 2d data object as stored in the astra-library. * data: can be either a MATLAB matrix containing the data. In that case the dimensions should match that of the geometry of the object. It can also be a single value, in which case the entire data will be set to that value. */ void astra_mex_data2d_store(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: input if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list. \n"); return; } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value. \n"); return; } int iDataID = (int)(mxGetScalar(prhs[1])); if (!(mexIsScalar(prhs[2]) || mxIsDouble(prhs[2]) || mxIsLogical(prhs[2]) || mxIsSingle(prhs[2]))) { mexErrMsgTxt("Data must be single, double or logical."); return; } if (mxIsSparse(prhs[2])) { mexErrMsgTxt("Data may not be sparse."); return; } // step2: get data object CFloat32Data2D* pDataObject = astra::CData2DManager::getSingleton().get(iDataID); if (!pDataObject || !pDataObject->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly.\n"); return; } // step3: insert data // fill with scalar value if (mexIsScalar(prhs[2])) { float32 fValue = (float32)mxGetScalar(prhs[2]); for (int i = 0; i < pDataObject->getSize(); ++i) { pDataObject->getData()[i] = fValue; } } else { // Check Data dimensions if (pDataObject->getWidth() != mxGetN(prhs[2]) || pDataObject->getHeight() != mxGetM(prhs[2])) { mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry. \n"); return; } const mwSize* dims = mxGetDimensions(prhs[2]); // logical data if (mxIsLogical(prhs[2])) { mxLogical* pbMatlabData = mxGetLogicals(prhs[2]); int i = 0; int col, row; for (col = 0; col < dims[1]; ++col) { for (row = 0; row < dims[0]; ++row) { pDataObject->getData2D()[row][col] = (float32)pbMatlabData[i]; ++i; } } // double data } else if (mxIsDouble(prhs[2])) { double* pdMatlabData = mxGetPr(prhs[2]); int i = 0; int col, row; for (col = 0; col < dims[1]; ++col) { for (row = 0; row < dims[0]; ++row) { pDataObject->getData2D()[row][col] = pdMatlabData[i]; ++i; } } // single data } else if (mxIsSingle(prhs[2])) { const float* pfMatlabData = (const float *)mxGetData(prhs[2]); int i = 0; int col, row; for (col = 0; col < dims[1]; ++col) { for (row = 0; row < dims[0]; ++row) { pDataObject->getData2D()[row][col] = pfMatlabData[i]; ++i; } } } else { ASTRA_ASSERT(false); } } } //----------------------------------------------------------------------------------------- /** geom = astra_mex_data2d('get_geometry', id); * * Fetch the geometry of a 2d data object stored in the astra-library. * id: identifier of the 2d data object as stored in the astra-library. * geom: MATLAB-struct containing information about the used geometry. */ void astra_mex_data2d_get_geometry(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // parse input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list. \n"); return; } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value. \n"); return; } int iDataID = (int)(mxGetScalar(prhs[1])); // fetch data object CFloat32Data2D* pDataObject = astra::CData2DManager::getSingleton().get(iDataID); if (!pDataObject || !pDataObject->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly.\n"); return; } // create output if (1 <= nlhs) { if (pDataObject->getType() == CFloat32Data2D::PROJECTION) { CFloat32ProjectionData2D* pDataObject2 = dynamic_cast(pDataObject); plhs[0] = configToStruct(pDataObject2->getGeometry()->getConfiguration()); } else if (pDataObject->getType() == CFloat32Data2D::VOLUME) { CFloat32VolumeData2D* pDataObject2 = dynamic_cast(pDataObject); plhs[0] = configToStruct(pDataObject2->getGeometry()->getConfiguration()); } } } //----------------------------------------------------------------------------------------- /** astra_mex_data2d('change_geometry', id, geom); * * Change the associated geometry of a 2d data object (volume or sinogram) * id: identifier of the 2d data object as stored in the astra-library. * geom: the new geometry struct, as created by astra_create_vol/proj_geom */ void astra_mex_data2d_change_geometry(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: check input if (nrhs < 3) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list. \n"); return; } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value. \n"); return; } // step2: get data object int iDataID = (int)(mxGetScalar(prhs[1])); CFloat32Data2D* pDataObject = astra::CData2DManager::getSingleton().get(iDataID); if (!pDataObject || !pDataObject->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly.\n"); return; } CFloat32ProjectionData2D* pSinogram = dynamic_cast(pDataObject); if (pSinogram) { // Projection data // Read geometry if (!mxIsStruct(prhs[2])) { mexErrMsgTxt("Argument 3 is not a valid MATLAB struct.\n"); } Config* cfg = structToConfig("ProjectionGeometry2D", prhs[2]); // FIXME: Change how the base class is created. (This is duplicated // in 'create' and Projector2D.cpp.) std::string type = cfg->self.getAttribute("type"); CProjectionGeometry2D* pGeometry; if (type == "sparse_matrix") { pGeometry = new CSparseMatrixProjectionGeometry2D(); } else if (type == "fanflat") { //CFanFlatProjectionGeometry2D* pFanFlatProjectionGeometry = new CFanFlatProjectionGeometry2D(); //pFanFlatProjectionGeometry->initialize(Config(node)); //m_pProjectionGeometry = pFanFlatProjectionGeometry; pGeometry = new CFanFlatProjectionGeometry2D(); } else if (type == "fanflat_vec") { pGeometry = new CFanFlatVecProjectionGeometry2D(); } else if (type == "parallel_vec") { pGeometry = new CParallelVecProjectionGeometry2D(); } else { pGeometry = new CParallelProjectionGeometry2D(); } if (!pGeometry->initialize(*cfg)) { mexErrMsgTxt("Geometry class not initialized. \n"); delete pGeometry; delete cfg; return; } // If data is specified, check dimensions if (pGeometry->getDetectorCount() != pSinogram->getDetectorCount() || pGeometry->getProjectionAngleCount() != pSinogram->getAngleCount()) { mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry. \n"); delete pGeometry; delete cfg; return; } // If ok, change geometry pSinogram->changeGeometry(pGeometry); delete pGeometry; delete cfg; return; } CFloat32VolumeData2D* pVolume = dynamic_cast(pDataObject); if (pVolume) { // Volume data // Read geometry if (!mxIsStruct(prhs[2])) { mexErrMsgTxt("Argument 3 is not a valid MATLAB struct.\n"); } Config* cfg = structToConfig("VolumeGeometry2D", prhs[2]); CVolumeGeometry2D* pGeometry = new CVolumeGeometry2D(); if (!pGeometry->initialize(*cfg)) { mexErrMsgTxt("Geometry class not initialized. \n"); delete cfg; delete pGeometry; return; } // If data is specified, check dimensions if (pGeometry->getGridColCount() != pVolume->getWidth() || pGeometry->getGridRowCount() != pVolume->getHeight()) { mexErrMsgTxt("The dimensions of the data do not match those specified in the geometry. \n"); delete cfg; delete pGeometry; return; } // If ok, change geometry pVolume->changeGeometry(pGeometry); delete cfg; delete pGeometry; } mexErrMsgTxt("Data object not found or not initialized properly.\n"); return; } //----------------------------------------------------------------------------------------- /** data = astra_mex_data2d('get', id); * * Fetch data from the astra-library to a MATLAB matrix. * id: identifier of the 2d data object as stored in the astra-library. * data: MATLAB data */ void astra_mex_data2d_get(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: check input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list. \n"); return; } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value. \n"); return; } // step2: get data object int iDataID = (int)(mxGetScalar(prhs[1])); CFloat32Data2D* pDataObject = astra::CData2DManager::getSingleton().get(iDataID); if (!pDataObject || !pDataObject->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly.\n"); return; } // create output if (1 <= nlhs) { plhs[0] = mxCreateDoubleMatrix(pDataObject->getHeight(), // # rows pDataObject->getWidth(), // # cols mxREAL); // datatype 64-bits double* out = mxGetPr(plhs[0]); int i = 0; int row, col; for (col = 0; col < pDataObject->getWidth(); ++col) { for (row = 0; row < pDataObject->getHeight(); ++row) { out[i] = pDataObject->getData2D()[row][col]; ++i; } } } } //----------------------------------------------------------------------------------------- /** data = astra_mex_data2d('get_single', id); * * Fetch data from the astra-library to a MATLAB matrix. * id: identifier of the 2d data object as stored in the astra-library. * data: MATLAB data */ void astra_mex_data2d_get_single(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // step1: check input if (nrhs < 2) { mexErrMsgTxt("Not enough arguments. See the help document for a detailed argument list. \n"); return; } if (!mxIsDouble(prhs[1])) { mexErrMsgTxt("Identifier should be a scalar value. \n"); return; } // step2: get data object int iDataID = (int)(mxGetScalar(prhs[1])); CFloat32Data2D* pDataObject = astra::CData2DManager::getSingleton().get(iDataID); if (!pDataObject || !pDataObject->isInitialized()) { mexErrMsgTxt("Data object not found or not initialized properly.\n"); return; } // create output if (1 <= nlhs) { mwSize dims[2]; dims[0] = pDataObject->getHeight(); dims[1] = pDataObject->getWidth(); plhs[0] = mxCreateNumericArray(2, dims, mxSINGLE_CLASS, mxREAL); float* out = (float *)mxGetData(plhs[0]); int i = 0; int row, col; for (col = 0; col < pDataObject->getWidth(); ++col) { for (row = 0; row < pDataObject->getHeight(); ++row) { out[i] = pDataObject->getData2D()[row][col]; ++i; } } } } //----------------------------------------------------------------------------------------- /** astra_mex_data2d('info'); * * Print information about all the 2d data objects currently stored in the astra-library. */ void astra_mex_data2d_info(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { mexPrintf("%s", astra::CData2DManager::getSingleton().info().c_str()); } //----------------------------------------------------------------------------------------- static void printHelp() { mexPrintf("Please specify a mode of operation.\n"); mexPrintf("Valid modes: get, get_single, delete, clear, set/store, create, get_geometry, change_geometry, info\n"); } //----------------------------------------------------------------------------------------- /** * ... = astra_mex_data2d(type,...); */ void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { // INPUT0: Mode string sMode = ""; if (1 <= nrhs) { sMode = mexToString(prhs[0]); } else { printHelp(); return; } initASTRAMex(); // SWITCH (MODE) if (sMode == std::string("get")) { astra_mex_data2d_get(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("get_single")) { astra_mex_data2d_get_single(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("delete")) { astra_mex_data2d_delete(nlhs, plhs, nrhs, prhs); } else if (sMode == "clear") { astra_mex_data2d_clear(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("store") || sMode == std::string("set")) { astra_mex_data2d_store(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("create")) { astra_mex_data2d_create(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("get_geometry")) { astra_mex_data2d_get_geometry(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("change_geometry")) { astra_mex_data2d_change_geometry(nlhs, plhs, nrhs, prhs); } else if (sMode == std::string("info")) { astra_mex_data2d_info(nlhs, plhs, nrhs, prhs); } else { printHelp(); } return; }