aleks 2 年 前
コミット
cb6002f522
64 ファイル変更10503 行追加0 行削除
  1. 85 0
      FBP.m
  2. 62 0
      Fitting.m
  3. 97 0
      Functions/Sources/Compile.m
  4. 110 0
      Functions/Sources/README.md
  5. 459 0
      Functions/Sources/backprojectionDD_mex/backprojectionDD_mex.cpp
  6. 41 0
      Functions/Sources/backprojectionDD_mex/backprojectionDD_mex.h
  7. BIN
      Functions/Sources/backprojectionDD_mex/backprojectionDD_mex.h.gch
  8. 31 0
      Functions/Sources/backprojectionDD_mex/backprojectionDD_mex.sln
  9. 147 0
      Functions/Sources/backprojectionDD_mex/backprojectionDD_mex.vcxproj
  10. 608 0
      Functions/Sources/backprojectionDDb_mex/backprojectionDDb_mex.cpp
  11. 114 0
      Functions/Sources/backprojectionDDb_mex/backprojectionDDb_mex.h
  12. 31 0
      Functions/Sources/backprojectionDDb_mex/backprojectionDDb_mex.sln
  13. 151 0
      Functions/Sources/backprojectionDDb_mex/backprojectionDDb_mex.vcxproj
  14. 25 0
      Functions/Sources/backprojectionDDb_mex_CUDA/backprojectionDDb_mex_CUDA.sln
  15. 103 0
      Functions/Sources/backprojectionDDb_mex_CUDA/backprojectionDDb_mex_CUDA.vcxproj
  16. 165 0
      Functions/Sources/backprojectionDDb_mex_CUDA/backprojectionDDb_mex_cuda.cpp
  17. 69 0
      Functions/Sources/backprojectionDDb_mex_CUDA/backprojectionDDb_mex_cuda.h
  18. 859 0
      Functions/Sources/backprojectionDDb_mex_CUDA/kernel.cu
  19. 455 0
      Functions/Sources/projectionDD_mex/projectionDD_mex.cpp
  20. 41 0
      Functions/Sources/projectionDD_mex/projectionDD_mex.h
  21. 31 0
      Functions/Sources/projectionDD_mex/projectionDD_mex.sln
  22. 147 0
      Functions/Sources/projectionDD_mex/projectionDD_mex.vcxproj
  23. 618 0
      Functions/Sources/projectionDDb_mex/projectionDDb_mex.cpp
  24. 113 0
      Functions/Sources/projectionDDb_mex/projectionDDb_mex.h
  25. 31 0
      Functions/Sources/projectionDDb_mex/projectionDDb_mex.sln
  26. 151 0
      Functions/Sources/projectionDDb_mex/projectionDDb_mex.vcxproj
  27. 824 0
      Functions/Sources/projectionDDb_mex_CUDA/kernel.cu
  28. 25 0
      Functions/Sources/projectionDDb_mex_CUDA/projectionDDb_mex_CUDA.sln
  29. 103 0
      Functions/Sources/projectionDDb_mex_CUDA/projectionDDb_mex_CUDA.vcxproj
  30. 163 0
      Functions/Sources/projectionDDb_mex_CUDA/projectionDDb_mex_cuda.cpp
  31. 69 0
      Functions/Sources/projectionDDb_mex_CUDA/projectionDDb_mex_cuda.h
  32. 125 0
      Functions/backprojection.m
  33. 334 0
      Functions/backprojectionDD.m
  34. 228 0
      Functions/backprojectionDDb.m
  35. 72 0
      Functions/dataPreprocess.m
  36. 109 0
      Functions/draw3d.m
  37. 38 0
      Functions/figureScreenSize.m
  38. 144 0
      Functions/filterProj.m
  39. 63 0
      Functions/phantom.m
  40. 234 0
      Functions/phantom3d.m
  41. 34 0
      Functions/projImage.m
  42. 150 0
      Functions/projection.m
  43. 329 0
      Functions/projectionDD.m
  44. 240 0
      Functions/projectionDDb.m
  45. 147 0
      Functions/projectionMicro.m
  46. 80 0
      Functions/readDicom.m
  47. 85 0
      Functions/saveData.m
  48. 49 0
      Functions/writeDcm.m
  49. 40 0
      Functions/writeDcmProj.m
  50. 144 0
      Functions/writeDicom.m
  51. 42 0
      Functions/writeDicomProj.m
  52. 674 0
      LICENSE
  53. 107 0
      MLEM.m
  54. 86 0
      Microcalcification.m
  55. 21 0
      Mikrokalcinacije.txt
  56. 93 0
      Parameters/ParameterSettings_GE.m
  57. 92 0
      Parameters/ParameterSettings_Hologic.m
  58. 106 0
      Parameters/ParameterSettings_MicroInsertion.m
  59. 106 0
      Parameters/ParameterSettings_Phantom.m
  60. 75 0
      ProjectionNoising.m
  61. 9 0
      README.md
  62. 290 0
      Reconstruction.m
  63. 118 0
      SART.m
  64. 111 0
      SIRT.m

+ 85 - 0
FBP.m

@@ -0,0 +1,85 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: April, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 FBP(proj,filterType,cutoff,filterOrder,parameter)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function makes the Filtered Backprojection of 2D images, 
+%     in order to reconstruct a certain number of slices.
+%     
+%     The geometry is for DBT with half cone-beam. All parameters are set 
+%     in "ParameterSettings" code. 
+%  
+%     INPUT:
+% 
+%     - proj = 2D projection images 
+%     - filterType = Filter type to be used
+%     - cutoff = Cut off frequency up to Nyquist
+%     - filterOrder = Butterworth filter order
+%     - parameter = Parameter of all geometry
+% 
+%     OUTPUT:
+% 
+%     - reconData3d{1} = Volume data reconstructed with FBP.
+%     - reconData3d{2} = Struct informations like - Filter and Bp Time
+% 
+%     Reference: Jiang Hsieh's book (second edition)
+%     Reference: Fessler, J. A.: Fundamentals of CT Reconstruction in 2D 
+%     and 3D. In: Brahme, A. (eds.) Comprehensive Biomedical Physics, 
+%     1st ed., vol. 2, pp 263-295. Elsevier, Netherlands (2014).
+%     doi:10.1016/B978-0-444-53632-7.00212-4.
+%     Reference: Fessler Book -> (http://web.eecs.umich.edu/~fessler/book/c-tomo.pdf)
+% 
+%     -----------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% Recon Code - Analytical reconstruction: FBP
+function reconData3d = FBP(proj,filterType,cutoff,filterOrder,parameter)
+
+global showinfo
+
+info.startDateAndTime = char(datetime('now','Format','MM-dd-yyyy''  ''HH:mm:ss'));
+info.reconMeth = filterType;
+
+if(showinfo)
+    fprintf('   ->   FBP... \n')
+end
+
+if(strcmp(filterType,'FBP'))
+    tStart = tic;
+    fprintf('Filtering projections   ')
+    proj = filterProj(proj,parameter,cutoff,filterOrder);  % Filter projections
+    info.FilterTime = num2str(toc(tStart));
+    info.CutOffFreq = num2str(cutoff);
+end
+
+tStart = tic;
+
+% Make the Backprojection
+fprintf('\nBack-projecting projections   ')
+reconData3d = backprojection(proj,parameter,[]);
+info.BackprojectTime = num2str(toc(tStart));
+
+% Store only interested slices and ROIs
+fprintf('\nStoring slices...')
+reconData3d = {reconData3d(parameter.iROI,parameter.jROI,parameter.sliceRange)};
+reconData3d{2,1} = info;
+
+end

+ 62 - 0
Fitting.m

@@ -0,0 +1,62 @@
+%% Author: Aleks Prša
+% Date: July, 2022
+% alex.prsa@gmail.com
+% =========================================================================
+%{
+% 
+%     DESCRIPTION:
+% 
+%     Program to obtain search parameters from projection images using
+%     optimisation function and least-square method.
+%
+%     Faculty of Mathematic and Physics
+%     University in Ljubljana
+%     Ljubljana, Slovenia
+%
+%}
+% =========================================================================
+%% Optimisation function
+
+It = [5.1; 5.5];                            % Anode current of each projection images
+
+Sb = [440.8, 479.2];                        % Signal of dark side of detector
+Sigmab = [12.95, 18.31];                    % Standard deviation od a signal of dark side of detector
+
+Sw = [27918, 30200];                        % Signal of bright side of detector
+Sigmaw = [335.18, 350.5];                   % Standard deviation od a signal of bright side of detector
+
+% Optimisation function - chi-square method
+R = @(x)( ...
+         (Sb(1)-x(1)*x(2)*x(5)*It(1)-x(3))^2/1.692^2  + (Sb(2)-x(1)*x(2)*x(5)*It(2)-x(3))^2/3.741^2 + ...
+         (Sw(1)-x(1)*x(2)*It(1)-x(3))^2/25^2 + (Sw(2)-x(1)*x(2)*It(2)-x(3))^2/25.823^2 + ...
+         (Sigmab(1)^2-x(1)^2*x(2)*x(5)*It(1)-x(4)^2)^2/0.247^2 + (Sigmab(2)^2-x(1)^2*x(2)*x(5)*It(2)-x(4)^2)^2/0.614^2 + ...
+         (Sigmaw(1)^2-x(1)^2*x(2)*It(1)-x(4)^2)^2/8.527^2 + (Sigmaw(2)^2-x(1)^2*x(2)*It(2)-x(4)^2)^2/3.1^2 ...
+         );
+
+x0 = [4, 1300, 400, 10, 0.0003];            % Initial parameters
+
+[x, r] = fminsearch(R, x0);
+
+Sb1 = x(1)*x(2)*x(5)*It(1) + x(3)
+Sb2 = x(1)*x(2)*x(5)*It(2) + x(3)
+Sw1 = x(1)*x(2)*It(1) + x(3)
+Sw2 = x(1)*x(2)*It(2) + x(3)
+
+Sigmab1 = sqrt(x(1)^2*x(2)*x(5)*It(1) + x(4)^2)
+Sigmab2 = sqrt(x(1)^2*x(2)*x(5)*It(2) + x(4)^2)
+Sigmaw1 = sqrt(x(1)^2*x(2)*It(1) + x(4)^2)
+Sigmaw2 = sqrt(x(1)^2*x(2)*It(2) + x(4)^2)
+
+fprintf('Optimal parameters:\n')
+fprintf('\ne0:') 
+disp(x(1))
+fprintf('\nalpha:') 
+disp(x(2))
+fprintf('\np:') 
+disp(x(3))
+fprintf('\nsigmap:') 
+disp(x(4))
+fprintf('\nepsilon:') 
+disp(x(5))
+fprintf('\nOstanek:') 
+disp(r)

+ 97 - 0
Functions/Sources/Compile.m

@@ -0,0 +1,97 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: April, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% 
+%     Reference: 
+%     https://www.codefull.net/2014/11/tutorial-use-cuda-code-in-matlab/
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% Compilation Code
+
+% Linux systems
+if isunix
+    
+    fprintf('--- Starting complilation ---\n');
+    
+    matlabRootPath = matlabroot;
+     
+    % ------------------------------------------
+    
+    pathVar{1} = 'backprojectionDD_mex';
+    pathVar{2} = 'projectionDD_mex';
+    
+    for k=1:2
+        mex('-largeArrayDims',[pathVar{k} filesep pathVar{k} '.cpp'],['-L' matlabRootPath '/bin/glnxa64'],'-outdir','..');
+    end
+    
+    % ------------------------------------------
+    
+    pathVar{1} = 'backprojectionDDb_mex';
+    pathVar{2} = 'projectionDDb_mex';
+    
+    for k=1:2
+        mex('-largeArrayDims',[pathVar{k} filesep pathVar{k} '.cpp'],['-L' matlabRootPath '/bin/glnxa64'],'CFLAGS="$CFLAGS -fopenmp"', 'LDFLAGS="$LDFLAGS -fopenmp"','-outdir','..');
+    end
+      
+    % ------------------------------------------
+
+    % https://www.mathworks.com/matlabcentral/answers/394830-nvcc-command-not-found
+    cudaPath = '/usr/local/cuda';
+    
+    if(~system('cd /usr/local/cuda'))
+        p = getenv('PATH');
+        setenv('PATH', [p ':' cudaPath '/bin'])
+        
+        pathVar{1} = 'backprojectionDDb_mex_CUDA';  sourceFilePath{1} = 'backprojectionDDb_mex_cuda.cpp';
+        pathVar{2} = 'projectionDDb_mex_CUDA';      sourceFilePath{2} = 'projectionDDb_mex_cuda.cpp';
+
+
+        for k=1:2
+            % https://stackoverflow.com/a/25197234/8682939
+            system(['nvcc -O3 -c ' pathVar{k} '/kernel.cu -Xcompiler -fPIC -I' matlabRootPath '/extern/include -I' matlabRootPath '/toolbox/distcomp/gpu/extern/include -I' cudaPath '/samples/common/inc']);
+            mex('-R2018a',[pathVar{k} filesep sourceFilePath{k}],'kernel.o',['-I' cudaPath '/include'],['-I' cudaPath '/samples/common/inc'],['-L' cudaPath '/lib64'],['-L' matlabRootPath '/bin/glnxa64'],'-lcudart', '-lcufft', '-lmwgpu','-outdir','..');    
+        end
+
+        system('rm kernel.o');
+    
+        flagCUDA = 1;
+    else
+        flagCUDA = 0;
+    end
+    
+    % ------------------------------------------
+    
+    fprintf('\n\n\n\nC++ files compiled.\n');
+    fprintf('C++ (OpenMP) files compiled.\n');
+    if(flagCUDA)
+        fprintf('CUDA files compiled.\n');
+    else
+        error('Either change CUDA toolkit path or install it.');
+    end
+    
+    
+    fprintf('\n--- Done!! --- \n');
+
+end
+
+
+
+

+ 110 - 0
Functions/Sources/README.md

@@ -0,0 +1,110 @@
+# Compiling mex/mex-CUDA files (Windows)
+
+If you want to compile mex-CUDA files, make sure to follow the **(optional)** steps.
+
+## What is necessary?
+
+ 1. Download [Visual Studio (VS) IDE](https://visualstudio.microsoft.com/vs/).
+
+ 2. A CUDA-capable GPU **(optional)**.
+ 
+ 3. Download [CUDA Toolkit](https://developer.nvidia.com/cuda-toolkit) **(optional)**.
+
+## How to install?
+
+ 1. Install VS with C++ components.
+
+ 2. Install CUDA Toolkit with VS integration (default installation) **(optional)**.
+
+## How to compile the codes?
+
+ 1. Open the project/solution on each folder.
+ 
+ 2. Change the paths according to your directories, for example:
+
+     * Configuration properties -> VC++ Directories -> Include Directories:
+        > C:\ProgramData\NVIDIA Corporation\CUDA Samples\v10.1\common\inc **(optional)**
+
+        > C:\Program Files\MATLAB\R2017b\extern\include
+
+     * Configuration properties -> Linker -> General:
+        > C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft
+
+## How to test?
+
+ 1. Add the release path on MATLAB, for example: 
+
+     * `addpath('C:\DBT-Reconstruction\Functions\Sources\projectionDDb_mex_CUDA\x64\Release')`
+     * `addpath('C:\DBT-Reconstruction\Functions\Sources\backprojectionDDb_mex_CUDA\x64\Release')`
+
+ 2. Or place the **.mexw64** files on the _Functions_ folder.
+
+ 3. Run the main _Reconstruction.m_ file to get the parameters and to load projections.
+
+ 4. Run the following command to get a simple Back-projection:
+     * `dataRecon3d_mex_CUDA = backprojectionDDb_mex_CUDA(double(dataProj),parameter);`
+
+ 5. You should get something like this:
+
+     > GPU Device 0: "GeForce GTX 1050 Ti" with compute capability 6.1 has 6 Multi-Processors and 4294967296 bytes of global memory
+
+
+**Note:** All these codes were developed and tested on:
+
+| Software      | Version 
+| ------------- |:-------------:
+| MATLAB            | 2017b 
+| Visual Studio IDE | 2019 
+| CUDA Toolkit      | 10.1 
+| GPU               | GeForce GTX 1050 Ti 
+| Windows           | 10 
+
+# Compiling mex/mex-CUDA files (Linux)
+
+If you want to compile mex-CUDA files, make sure to follow the **(optional)** steps.
+
+## What is necessary?
+
+ 1. A CUDA-capable GPU **(optional)**.
+ 
+ 2. Download [CUDA Toolkit](https://developer.nvidia.com/cuda-toolkit) ([Tutorial](https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html)) **(optional)**.
+ 
+ ## How to compile the codes?
+ 
+ 1. Run the _Compile.m_ file.
+ 2. Make sure to set the variable `cudaPath` with your [cuda path](https://stackoverflow.com/a/54953476/8682939) **(optional)**. 
+ 
+ ## How to test?
+ 
+ 1. The **.mexa64** files will be placed on the _Functions_ folder.
+
+ 3. Run the main _Reconstruction.m_ file to get the parameters and to load projections.
+
+ 4. Run the following command to get a simple Back-projection:
+     * `dataRecon3d_mex_CUDA = backprojectionDDb_mex_CUDA(double(dataProj),parameter);`
+
+ 5. You should get something like this:
+
+     > GPU Device 0: "GeForce GTX 1050 Ti" with compute capability 6.1 has 6 Multi-Processors and 4294967296 bytes of global memory
+
+
+**Note:** All these codes were developed and tested on:
+
+| Software      | Version 
+| ------------- |:-------------:
+| MATLAB            | 2017b 
+| CUDA Toolkit      | 10.1 
+| GPU               | GeForce GTX 1050 Ti 
+| Ubuntu            | 19.04 
+ 
+
+## Having problems?
+
+Please report issues [here](https://github.com/LAVI-USP/DBT-Reconstruction/issues). I also can send you some already compiled mex files. 
+
+---
+
+Laboratory of Computer Vision ([Lavi](http://iris.sel.eesc.usp.br/lavi/)) 
+Department of Electrical and Computer Engineering 
+São Carlos School of Engineering, University of São Paulo 
+São Carlos - Brazil 

+ 459 - 0
Functions/Sources/backprojectionDD_mex/backprojectionDD_mex.cpp

@@ -0,0 +1,459 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: September, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 backprojectionDD_mex(proj,param)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function reconstruct the 3D volume from projections, based on
+%     the Distance-Driven principle. It works by calculating the overlap
+%     in X and Y axis of the volume and the detector boundaries.
+%     The geometry is for DBT with half cone-beam. All parameters are set
+%     in "ParameterSettings" code.
+%
+%     INPUT:
+%
+%     - proj = 2D projections for each angle
+%     - param = Parameter of all geometry
+%
+%     OUTPUT:
+%
+%     - data3d = reconstructed volume.
+%
+%     Reference: Three-Dimensional Digital Tomosynthesis - Yulia
+%     Levakhina (2014), Cap 3.6 and 3.7.
+%
+%     Original Paper: De Man, Bruno, and Samit Basu. "Distance-driven
+%     projection and backprojection in three dimensions." Physics in
+%     Medicine & Biology (2004).
+%
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Distance Driven Back-projection Code
+*/
+
+#include "backprojectionDD_mex.h"
+
+/****************************************************************************
+* function: mexFunction() - Matlab interface function.						*
+* INPUTS:																	*
+*   nlhs - Number of expected output mxArrays, specified as an integer.		*
+*   plhs[] - Array of pointers to the expected mxArray output arguments.	*
+*   nrhs - Number of input mxArrays, specified as an integer.				*
+*   prhs[] - Array of pointers to the mxArray input arguments.				*
+****************************************************************************/
+
+void mexFunction(int nlhs, mxArray *plhs[],
+	int nrhs, const mxArray *prhs[]) {
+
+
+	/* Check for proper number of arguments */
+	if (nrhs != 2) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:nargin",
+			"projection_mex requires two input arguments.");
+	}
+	if (nlhs != 1) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:nargout",
+			"projection_mex requires one output argument.");
+	}
+
+	/* Check if the input is of proper type */
+	if (!mxIsSingle(prhs[0])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument has to be single.");
+	}
+	if (!mxIsStruct(prhs[1])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"Second argument has to be a struct.");
+	}
+
+
+	float* pProj = (float*)mxGetPr(prhs[0]);
+	double* pTubeAngleD = mxGetPr(mxGetField(prhs[1], 0, "tubeDeg"));
+	double* pDetAngleD = mxGetPr(mxGetField(prhs[1], 0, "detectorDeg"));
+
+	const int nProj = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nProj"));
+
+	const int nPixX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nx"));
+	const int nPixY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "ny"));
+	const int nSlices = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nz"));
+	const int nDetX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nu"));
+	const int nDetY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nv"));
+
+	const float dx = (const float)mxGetScalar(mxGetField(prhs[1], 0, "dx"));
+	const float dy = (const float)mxGetScalar(mxGetField(prhs[1], 0, "dy"));
+	const float dz = (const float)mxGetScalar(mxGetField(prhs[1], 0, "dz"));
+	const float du = (const float)mxGetScalar(mxGetField(prhs[1], 0, "du"));
+	const float dv = (const float)mxGetScalar(mxGetField(prhs[1], 0, "dv"));
+
+	const float DSD = (const float)mxGetScalar(mxGetField(prhs[1], 0, "DSD"));
+	const float DDR = (const float)mxGetScalar(mxGetField(prhs[1], 0, "DDR"));
+	const float DAG = (const float)mxGetScalar(mxGetField(prhs[1], 0, "DAG"));
+
+	mwSize NRow = mxGetM(prhs[0]);
+	mwSize NCol = mxGetN(prhs[0]);
+	mwSize NElemVol = mxGetNumberOfElements(prhs[0]);
+
+	/* Check if the input is in proper size */
+	if (NRow != nDetY) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument needs to have the same number of rows as in the configuration file.");
+	}
+
+	if (NCol / nProj != nDetX) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument needs to have the same number of columns as in the configuration file.");
+	}
+
+	// Cast double vectors to single 
+	float* const pTubeAngle = (float*)mxMalloc(nProj * sizeof(float));
+	float* const pDetAngle = (float*)mxMalloc(nProj * sizeof(float));
+
+	for (int n = 0; n < nProj; n++) {
+		pTubeAngle[n] = (float)pTubeAngleD[n];
+		pDetAngle[n] = (float)pDetAngleD[n];
+	}
+
+	//mexPrintf("Nx:%d Ny:%d Nz:%d \nNu:%d Nv:%d \nDx:%.2f Dy:%.2f Dz:%.2f \nDu:%.2f Dv:%.2f \nDSD:%.2f DDR:%.2f \n", nPixX, nPixY, nSlices, nDetX, nDetY, dx, dy, dz, du, dv, DSD, DDR);
+
+	const int nDetXMap = nDetX + 1;
+	const int nDetYMap = nDetY + 1;
+	const int nPixXMap = nPixX + 1;
+	const int nPixYMap = nPixY + 1;
+
+	// Memory allocation for projections coordinates
+	float* const pDetX = (float*)mxMalloc(nDetXMap * sizeof(float));
+	float* const pDetY = (float*)mxMalloc(nDetYMap * sizeof(float));
+	float* const pDetZ = (float*)mxMalloc(nDetYMap * sizeof(float));
+	float* const pObjX = (float*)mxMalloc(nPixXMap * sizeof(float));
+	float* const pObjY = (float*)mxMalloc(nPixYMap * sizeof(float));
+	float* const pObjZ = (float*)mxMalloc(nSlices * sizeof(float));
+
+	// Memory allocation for mapped coordinates
+	float* const pDetmY = (float*)mxMalloc(nDetYMap * sizeof(float));
+	float* const pDetmX = (float*)mxMalloc(nDetYMap * nDetXMap * sizeof(float));
+	float* const pPixmX = (float*)mxMalloc(nPixXMap * sizeof(float));
+	float* const pPixmY = (float*)mxMalloc(nPixYMap * sizeof(float));
+
+
+	// Memory allocation for rotated detecto coords
+	float* const pRdetY = (float*)mxMalloc(nDetYMap * sizeof(float));
+	float* const pRdetZ = (float*)mxMalloc(nDetYMap * sizeof(float));
+
+
+	// Map detector and object boudaries
+	for (int k = nDetX, ind = 0; k >= 0; k--, ind++)					pDetX[ind] = k * du;
+	for (int k = -nDetY / 2, ind = 0; k <= nDetY / 2; k++, ind++)		pDetY[ind] = k * dv;
+	for (int k = 0; k <= nDetY; k++)								pDetZ[k] = 0;
+	for (int k = nPixX, ind = 0; k >= 0; k--, ind++)				pObjX[ind] = k * dx;
+	for (int k = -nPixY / 2, ind = 0; k <= nPixY / 2; k++, ind++)	pObjY[ind] = k * dy;
+	for (int k = 0, ind = 0; k <= nSlices; k++, ind++)				pObjZ[ind] = k * dz + DAG + dz / 2;
+
+
+	// X - ray tube initial position
+	float tubeX = 0;
+	float tubeY = 0;
+	float tubeZ = DSD;
+
+	// Iso - center position
+	float isoY = 0;
+	float isoZ = DDR;
+
+
+	// Allocate memory for temp projection variable
+	float* const pVolumet = (float*)mxMalloc(nPixY *nPixX * nSlices * sizeof(float));
+
+
+	// Initiate temp volume with zeros
+	for (int z = 0; z < nSlices; z++)
+		for (int x = 0; x < nPixX; x++)
+			for (int y = 0; y < nPixY; y++)
+				pVolumet[(z*nPixX*nPixY) + (x*nPixY) + y] = 0;
+
+
+	// Allocate memory for flipped projection
+	float* const pProjf = (float*)mxMalloc(nDetY *nDetX * nProj * sizeof(float));
+
+	// Flip projection X (Img coord is reverse to Global)
+	for (int p = 0; p < nProj; p++)
+		for (int x = 0, x_inv = nDetX - 1; x < nDetX; x++, x_inv--)
+			for (int y = 0; y < nDetY; y++)
+				pProjf[(p*nDetX*nDetY) + (x*nDetY) + y] = pProj[(p*nDetX*nDetY) + (x_inv*nDetY) + y];
+
+
+	// For each projection
+	for (int p = 0; p < nProj; p++) {
+
+		// Get specif tube angle for the projection
+		float theta = pTubeAngle[p] * M_PI / 180.f;
+
+		// Get specif detector angle for the projection
+		float phi = pDetAngle[p] * M_PI / 180.f;
+
+
+		// Tubre rotation
+		float rtubeY = ((tubeY - isoY)*(float)cos(theta) - (tubeZ - isoZ)*(float)sin(theta)) + isoY;
+		float rtubeZ = ((tubeY - isoY)*(float)sin(theta) + (tubeZ - isoZ)*(float)cos(theta)) + isoZ;
+
+		// Detector rotation
+		for (int v = 0; v < nDetYMap; v++) {
+			pRdetY[v] = ((pDetY[v] - isoY)*(float)cos(phi) - (pDetZ[v] - isoZ)*(float)sin(phi)) + isoY;
+			pRdetZ[v] = ((pDetY[v] - isoY)*(float)sin(phi) + (pDetZ[v] - isoZ)*(float)cos(phi)) + isoZ;
+		}
+
+
+		// Map detector onto XY plane(Inside proj loop in case detector rotates)
+		mapp2xy(pDetmX, pDetmY, tubeX, rtubeY, rtubeZ, pDetX, pRdetY, pRdetZ, nDetXMap, nDetYMap);
+
+
+		// Pixel start index and increment
+		int detIstart = 0;
+		int detIinc = 1;
+
+		// Mapped detector length
+		float deltaDetmY = pDetmY[detIstart + detIinc] - pDetmY[detIstart];
+
+
+		// For each slice
+		for (int z = 0; z < nSlices; z++) {
+
+			// Tmp Z coords value for Y direction
+			float* const pObjZt = (float*)mxMalloc(nPixYMap * sizeof(float));
+
+			// Tmp Pixel X mapped coords
+			float* const pPixmXt = (float*)mxMalloc(nPixYMap * nPixXMap * sizeof(float));
+
+			// Get specif Z coord value for each slice
+			for (int k = 0; k < nPixYMap; k++)	pObjZt[k] = pObjZ[z];
+
+			// Map slice onto XY plane
+			mapp2xy(pPixmXt, pPixmY, tubeX, rtubeY, rtubeZ, pObjX, pObjY, pObjZt, nPixXMap, nPixYMap);
+
+			// Flip X(Img coord is reverse to Global)
+			for (int x = 0, x_inv = nPixXMap - 1; x < nPixXMap; x++, x_inv--)
+				pPixmX[x] = pPixmXt[x_inv*nPixYMap];
+
+			// Free temp variables
+			mxFree(pObjZt);
+			mxFree(pPixmXt);
+
+			// Pixel start index and increment
+			int pixIstart = 0;
+			int pixIinc = 1;
+
+			// Mapped pixel length
+			float deltaPixmX = pPixmX[pixIstart + pixIinc] - pPixmX[pixIstart];
+			float deltaPixmY = pPixmY[pixIstart + pixIinc] - pPixmY[pixIstart];
+
+			// Start pixel and detector indices
+			int detIndY = detIstart;
+			int pixIndY = pixIstart;
+
+			// Case 1
+			// Find first detector overlap maped with pixel maped on Y
+			if (pDetmY[detIndY] - pPixmY[pixIstart] < -deltaDetmY)
+				while (pDetmY[detIndY] - pPixmY[pixIstart] < -deltaDetmY)
+					detIndY = detIndY + detIinc;
+
+			else
+				// Case 2
+				// Find first pixel overlap maped with detector maped on Y
+				if (pDetmY[detIstart] - pPixmY[pixIndY] > deltaPixmY)
+					while (pDetmY[detIstart] - pPixmY[pixIndY] > deltaPixmY)
+						pixIndY = pixIndY + pixIinc;
+
+			float moving_left_boundaryY = NULL;
+
+			// Get the left coordinate of the first overlap on Y axis
+			if (pDetmY[detIndY] < pPixmY[pixIndY])
+				moving_left_boundaryY = pPixmY[pixIndY];
+			else
+				moving_left_boundaryY = pDetmY[detIndY];
+
+
+			// Allocate memory for specif row of X map detector coords
+			float* const pDetmXrow = (float*)mxMalloc(nDetXMap * sizeof(float));
+
+			float overLapY = NULL;
+
+			// Loop over Y intersections
+			while ((detIndY < nDetY) && (pixIndY < nPixY)) {
+
+				float alpha = (float)atan((pDetmY[detIndY] + (deltaDetmY / 2) - rtubeY) / rtubeZ);
+
+				// Case A, when you jump to the next detector boundarie but stay
+				// in the same pixel
+				if (pDetmY[detIndY + 1] <= pPixmY[pixIndY + 1])
+					overLapY = (pDetmY[detIndY + 1] - moving_left_boundaryY) / deltaDetmY; // Normalized overlap Calculation
+
+				else
+					// Case B, when you jump to the next pixel boundarie but stay
+					// in the same detector
+					overLapY = (pPixmY[pixIndY + 1] - moving_left_boundaryY) / deltaDetmY; // Normalized overlap Calculation
+
+																						   //										***** X overlap *****
+				int detIndX = detIstart;
+				int pixIndX = pixIstart;
+
+
+				// Get row / coll of X flipped, which correspond to that Y overlap det
+				for (int x = 0, x_inv = nDetXMap - 1; x < nDetXMap; x++, x_inv--)
+					pDetmXrow[x] = pDetmX[(x_inv*nDetYMap) + detIndY];
+
+				// Mapped detecor length on X
+				float deltaDetmX = pDetmXrow[detIstart + detIinc] - pDetmXrow[detIstart];
+
+				// Case 1
+				// Find first detector overlap maped with pixel maped on X
+				if (pDetmXrow[detIndX] - pPixmX[pixIstart] < -deltaDetmX)
+					while (pDetmXrow[detIndX] - pPixmX[pixIstart] < -deltaDetmX)
+						detIndX = detIndX + detIinc;
+
+				else
+					// Case 2
+					// Find first pixel overlap maped with detector maped on X
+					if (pDetmXrow[detIstart] - pPixmX[pixIndX] > deltaPixmX)
+						while (pDetmXrow[detIstart] - pPixmX[pixIndY] > deltaPixmX)
+							pixIndX = pixIndX + pixIinc;
+
+				float moving_left_boundaryX = NULL;
+
+				// Get the left coordinate of the first overlap on X axis
+				if (pDetmXrow[detIndX] < pPixmX[pixIndX])
+					moving_left_boundaryX = pPixmX[pixIndX];
+				else
+					moving_left_boundaryX = pDetmXrow[detIndX];
+
+
+				// Loop over X intersections
+				while ((detIndX < nDetX) && (pixIndX < nPixX)) {
+
+					float gamma = (float)atan((pDetmXrow[detIndX] + (deltaDetmX / 2) - tubeX) / rtubeZ);
+
+					// Case A, when you jump to the next detector boundarie but stay
+					// in the same pixel
+					if (pDetmXrow[detIndX + 1] <= pPixmX[pixIndX + 1]) {
+
+						float overLapX = (pDetmXrow[detIndX + 1] - moving_left_boundaryX) / deltaDetmX; // Normalized overlap Calculation
+
+						pVolumet[(z*nPixX*nPixY) + (pixIndX*nPixY) + pixIndY] += overLapX * overLapY * pProjf[(p*nDetX*nDetY) + (detIndX *nDetY) + detIndY] * dz / ((float)cos(alpha)*(float)cos(gamma));
+
+						detIndX = detIndX + detIinc;
+						moving_left_boundaryX = pDetmXrow[detIndX];
+					}
+					else {
+						// Case B, when you jump to the next pixel boundarie but stay
+						// in the same detector
+
+						float overLapX = (pPixmX[pixIndX + 1] - moving_left_boundaryX) / deltaDetmX; // Normalized overlap Calculation
+
+						pVolumet[(z*nPixX*nPixY) + (pixIndX*nPixY) + pixIndY] += overLapX * overLapY * pProjf[(p*nDetX*nDetY) + (detIndX *nDetY) + detIndY] * dz / ((float)cos(alpha)*(float)cos(gamma));
+
+						pixIndX = pixIndX + pixIinc;
+						moving_left_boundaryX = pPixmX[pixIndX];
+
+					}
+
+				}
+				//										***** Back to Y overlap *****
+
+				// Case A, when you jump to the next detector boundarie but stay
+				// in the same pixel
+				if (pDetmY[detIndY + 1] <= pPixmY[pixIndY + 1]) {
+					detIndY = detIndY + detIinc;
+					moving_left_boundaryY = pDetmY[detIndY];
+				}
+				else {
+					// Case B, when you jump to the next pixel boundarie but stay
+					// in the same detector
+					pixIndY = pixIndY + pixIinc;
+					moving_left_boundaryY = pPixmY[pixIndY];
+				}
+
+			} // Y Overlap loop
+
+			// Free memory
+			mxFree(pDetmXrow);
+
+		} // Loop end slices
+
+	} // Loop end Projections
+
+
+	// Free memory
+	mxFree(pProjf);
+	mxFree(pDetX);
+	mxFree(pDetY);
+	mxFree(pDetZ);
+	mxFree(pObjX);
+	mxFree(pObjY);
+	mxFree(pObjZ);
+	mxFree(pDetmY);
+	mxFree(pDetmX);
+	mxFree(pPixmX);
+	mxFree(pPixmY);
+	mxFree(pRdetY);
+	mxFree(pRdetZ);
+
+	// Output memory allocation
+	mwSize dims[3] = { nPixY,nPixX,nSlices };
+	plhs[0] = mxCreateNumericArray(3, dims, mxSINGLE_CLASS, mxREAL);
+	float * pVolume = (float*)mxGetData(plhs[0]);
+
+	// Flip volume back X (Img coord is reverse to Global)
+	for (int z = 0; z < nSlices; z++)
+		for (int x = 0, x_inv = nPixX - 1; x < nPixX; x++, x_inv--)
+			for (int y = 0; y < nPixY; y++)
+				pVolume[(z*nPixX*nPixY) + (x*nPixY) + y] = pVolumet[(z*nPixX*nPixY) + (x_inv*nPixY) + y] / (float) nProj;
+
+	mxFree(pVolumet);
+
+	return;
+}
+
+
+
+// Map on XY plane
+void mapp2xy(float* const pXmapp,
+	float* const pYmapp,
+	float tubeX,
+	float tubeY,
+	float tubeZ,
+	float * const pXcoord,
+	float * const pYcoord,
+	float * const pZcoord,
+	const int nXelem,
+	const int nYelem) {
+
+
+	for (int x = 0; x < nXelem; x++)
+		for (int y = 0; y < nYelem; y++) {
+
+			int ind = (x*nYelem) + y;
+			pXmapp[ind] = pXcoord[x] + pZcoord[y] * (pXcoord[x] - tubeX) / (tubeZ - pZcoord[y]);
+
+			if (x == 0)
+				pYmapp[y] = pYcoord[y] + pZcoord[y] * (pYcoord[y] - tubeY) / (tubeZ - pZcoord[y]);
+		}
+
+
+	return;
+}

+ 41 - 0
Functions/Sources/backprojectionDD_mex/backprojectionDD_mex.h

@@ -0,0 +1,41 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: September, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This is the header function
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Distance Driven Back-projection Code
+*/
+
+//typedef double user_dataType;
+//typedef float user_dataType;
+//#define user_mexdataType mxDOUBLE_CLASS
+//#define user_mexdataType mxSINGLE_CLASS
+
+#include "mex.h"
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+void mapp2xy(float* const pXmapp, float* const pYmapp, float tubeX, float tubeY, float tubeZ, float * const pXcoord, float * const pYcoord, float * const pZcoord, const int nXelem, const int nYelem);

BIN
Functions/Sources/backprojectionDD_mex/backprojectionDD_mex.h.gch


+ 31 - 0
Functions/Sources/backprojectionDD_mex/backprojectionDD_mex.sln

@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29025.244
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "backprojectionDD_mex", "backprojectionDD_mex.vcxproj", "{54C3EC8A-8E69-49E1-9146-49F1023FDFE5}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{54C3EC8A-8E69-49E1-9146-49F1023FDFE5}.Debug|x64.ActiveCfg = Debug|x64
+		{54C3EC8A-8E69-49E1-9146-49F1023FDFE5}.Debug|x64.Build.0 = Debug|x64
+		{54C3EC8A-8E69-49E1-9146-49F1023FDFE5}.Debug|x86.ActiveCfg = Debug|Win32
+		{54C3EC8A-8E69-49E1-9146-49F1023FDFE5}.Debug|x86.Build.0 = Debug|Win32
+		{54C3EC8A-8E69-49E1-9146-49F1023FDFE5}.Release|x64.ActiveCfg = Release|x64
+		{54C3EC8A-8E69-49E1-9146-49F1023FDFE5}.Release|x64.Build.0 = Release|x64
+		{54C3EC8A-8E69-49E1-9146-49F1023FDFE5}.Release|x86.ActiveCfg = Release|Win32
+		{54C3EC8A-8E69-49E1-9146-49F1023FDFE5}.Release|x86.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {E888AEA0-BA22-457F-98BA-B78A38CA7CA0}
+	EndGlobalSection
+EndGlobal

+ 147 - 0
Functions/Sources/backprojectionDD_mex/backprojectionDD_mex.vcxproj

@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="backprojectionDD_mex.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="backprojectionDD_mex.h" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <ProjectGuid>{54c3ec8a-8e69-49e1-9146-49f1023fdfe5}</ProjectGuid>
+    <RootNamespace>backprojectionDD_mex</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <TargetExt>.mexw64</TargetExt>
+    <IncludePath>C:\Program Files\MATLAB\R2017b\extern\include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <TargetExt>.mexw64</TargetExt>
+    <IncludePath>C:\Program Files\MATLAB\R2017b\extern\include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libut.lib;libmx.lib;libmex.lib;libmat.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalOptions>/export:mexFunction %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libut.lib;libmx.lib;libmex.lib;libmat.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalOptions>/export:mexFunction %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 608 - 0
Functions/Sources/backprojectionDDb_mex/backprojectionDDb_mex.cpp

@@ -0,0 +1,608 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: March, 2019
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 backprojectionDDb_mex(proj,param)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function reconstruct the 3D volume from projections, based on
+%     the Distance-Driven principle. It works by calculating the overlap
+%     in X and Y axis of the volume and the detector boundaries.
+%     The geometry is for DBT with half cone-beam. All parameters are set
+%     in "ParameterSettings" code.
+%
+%     INPUT:
+%
+%     - proj = 2D projections for each angle
+%     - param = Parameter of all geometry
+%
+%     OUTPUT:
+%
+%     - data3d = reconstructed volume.
+%
+%     Reference:
+%     - Branchless Distance Driven Projection and Backprojection,
+%     Samit Basu and Bruno De Man (2006)
+%     - GPU Acceleration of Branchless Distance Driven Projection and
+%     Backprojection, Liu et al (2016)
+%     - GPU-Based Branchless Distance-Driven Projection and Backprojection,
+%     Liu et al (2017)
+%     - A GPU Implementation of Distance-Driven Computed Tomography,
+%     Ryan D. Wagner (2017)
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Back-projection Branchless Distance Driven Code (CPU-Multithread)
+*/
+
+#include "backprojectionDDb_mex.h"
+
+/****************************************************************************
+* function: mexFunction() - Matlab interface function.						*
+* INPUTS:																	*
+*   nlhs - Number of expected output mxArrays, specified as an integer.		*
+*   plhs[] - Array of pointers to the expected mxArray output arguments.	*
+*   nrhs - Number of input mxArrays, specified as an integer.				*
+*   prhs[] - Array of pointers to the mxArray input arguments.				*
+****************************************************************************/
+
+void mexFunction(int nlhs, mxArray *plhs[],
+	int nrhs, const mxArray *prhs[]) {
+
+
+	/* Check for proper number of arguments */
+	if (nrhs != 2) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:nargin",
+			"backprojection_mex requires two input arguments.");
+	}
+	if (nlhs != 1) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:nargout",
+			"backprojection_mex requires one output argument.");
+	}
+
+	/* Check if the input is of proper type */
+	if (!mxIsDouble(prhs[0])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument has to be double.");
+	}
+	if (!mxIsStruct(prhs[1])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"Second argument has to be a struct.");
+	}
+
+	double* const pProj = (double*)(mxGetPr(prhs[0])); // This variable has to come as single/double
+	double* const pTubeAngleD = mxGetPr(mxGetField(prhs[1], 0, "tubeDeg"));
+	double* const pDetAngleD = mxGetPr(mxGetField(prhs[1], 0, "detectorDeg"));
+
+
+	const int unsigned nProj = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nProj"));
+
+	const int unsigned nPixX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nx"));
+	const int unsigned nPixY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "ny"));
+	const int unsigned nSlices = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nz"));
+	const int unsigned nDetX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nu"));
+	const int unsigned nDetY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nv"));
+
+	const double dx = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dx"));
+	const double dy = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dy"));
+	const double dz = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dz"));
+	const double du = (const double)mxGetScalar(mxGetField(prhs[1], 0, "du"));
+	const double dv = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dv"));
+
+	const double DSD = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DSD"));
+	const double DDR = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DDR"));
+	const double DAG = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DAG"));
+
+	mwSize NRow = mxGetM(prhs[0]);
+	mwSize NCol = mxGetN(prhs[0]);
+	mwSize NElemVol = mxGetNumberOfElements(prhs[0]);
+
+	/* Check if the input is in proper size */
+	if (NRow != nDetY) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument needs to have the same number of rows as in the configuration file.");
+	}
+
+	if (NCol / nProj != nDetX) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument needs to have the same number of columns as in the configuration file.");
+	}
+
+	// Cast single vectors to double
+	double* const pTubeAngle = (double*)mxMalloc(nProj * sizeof(double));
+	double* const pDetAngle = (double*)mxMalloc(nProj * sizeof(double));
+
+	for (unsigned int n = 0; n < nProj; n++) {
+		pTubeAngle[n] = (double)pTubeAngleD[n];
+		pDetAngle[n] = (double)pDetAngleD[n];
+	}
+
+	//mexPrintf("Nx:%d Ny:%d Nz:%d \nNu:%d Nv:%d \nDx:%.2f Dy:%.2f Dz:%.2f \nDu:%.2f Dv:%.2f \nDSD:%.2f DDR:%.2f \n", nPixX, nPixY, nSlices, nDetX, nDetY, dx, dy, dz, du, dv, DSD, DDR);
+
+
+	// Output memory allocation
+	mwSize dims[3] = { nPixY,nPixX,nSlices };
+
+	plhs[0] = mxCreateNumericArray(3, dims, mxDOUBLE_CLASS, mxREAL);
+	double* const pVolume = (double*)mxGetData(plhs[0]);
+
+	backprojectionDDb(pVolume, pProj, pTubeAngle, pDetAngle, nProj, nPixX, nPixY, nSlices, nDetX, nDetY, dx, dy, dz, du, dv, DSD, DDR, DAG);
+
+	mxFree(pTubeAngle);
+	mxFree(pDetAngle);
+
+}
+
+
+// CPU Branchless Distance-driven back-projection
+void backprojectionDDb(double* const pVolume,
+	double* const pProj,
+	double* const pTubeAngle,
+	double* const pDetAngle,
+	const  int nProj,
+	const  int nPixX,
+	const  int nPixY,
+	const  int nSlices,
+	const  int nDetX,
+	const  int nDetY,
+	const double dx,
+	const double dy,
+	const double dz,
+	const double du,
+	const double dv,
+	const double DSD,
+	const double DDR,
+	const double DAG)
+{
+
+	nThreads = omp_get_max_threads();
+
+	mexPrintf("CPU running with maximum number of threads: %d \n", nThreads);
+	
+	clock_t time;
+
+	time = clock();
+	
+	// Number of mapped detector and pixels
+	const int nDetXMap = nDetX + 1;
+	const int nDetYMap = nDetY + 1;
+	const int nPixXMap = nPixX + 1;
+	const int nPixYMap = nPixY + 1;
+
+
+	// Allocate memory
+	double* const pSliceI = (double*)mxMalloc(nPixXMap*nPixYMap * sizeof(double));
+
+
+	// Pointer for projections coordinates
+	// Allocate memory for projections coordinates
+	double* const pDetX = (double*)mxMalloc(nDetXMap * sizeof(double));
+	double* const pDetY = (double*)mxMalloc(nDetYMap * sizeof(double));
+	double* const pDetZ = (double*)mxMalloc(nDetYMap * sizeof(double));
+	double* const pObjX = (double*)mxMalloc(nPixXMap * sizeof(double));
+	double* const pObjY = (double*)mxMalloc(nPixYMap * sizeof(double));
+	double* const pObjZ = (double*)mxMalloc(nSlices * sizeof(double));
+
+
+	// Pointer for mapped coordinates
+	// Allocate memory for mapped coordinates
+	double* const pDetmY = (double*)mxMalloc(nDetYMap * sizeof(double));
+	double* const pDetmX = (double*)mxMalloc(nDetYMap * nDetXMap * sizeof(double));
+
+
+
+	// Pointer for rotated detector coords
+	// Allocate memory for rotated detector coords
+	double* const pRdetY = (double*)mxMalloc(nDetYMap * sizeof(double));
+	double* const pRdetZ = (double*)mxMalloc(nDetYMap * sizeof(double));
+
+
+	// Map detector and object boudaries
+	mapBoudaries(pDetX, nDetXMap, (double)nDetX, -du, 0.0);
+
+	mapBoudaries(pDetY, nDetYMap, nDetY / 2.0, dv, 0.0);
+
+	mapBoudaries(pDetZ, nDetYMap, 0.0, 0.0, 0.0);
+
+	mapBoudaries(pObjX, nPixXMap, (double)nPixX, -dx, 0.0);
+
+	mapBoudaries(pObjY, nPixYMap, nPixY / 2.0, dy, 0.0);
+
+	mapBoudaries(pObjZ, nSlices, 0.0, dz, DAG + (dz / 2.0));
+
+
+	// X - ray tube initial position
+	double tubeX = 0;
+	double tubeY = 0;
+	double tubeZ = DSD;
+
+	// Iso - center position
+	double isoY = 0;
+	double isoZ = DDR;
+
+
+	// Allocate memory for temp projection variable
+	double* const pProjt = (double*)mxMalloc(nDetYMap *nDetXMap * nProj * sizeof(double));
+	double* const pVolumet = (double*)mxMalloc(nPixY *nPixX * nSlices * sizeof(double));
+
+	
+	// Initiate variables value with 0
+	for (int nz = 0; nz < nSlices; nz++)
+		for (int x = 0; x < nPixX; x++)
+			for (int y = 0; y < nPixY; y++)
+				pVolumet[(nz*nPixY *nPixX) + (x*nPixY) + y] = 0;
+	
+
+	// Integration of 2D slices over the whole volume
+	// (S.1.Integration. - Liu et al(2017))
+	/*
+		
+		2 - D image
+
+		 -->J
+		|	-------------
+		v	|			|
+		I	|			|
+			|			|
+			|			|
+			-------------
+	*/
+	
+	
+	// Initialize first column and row with zeros
+	for (int p = 0; p < nProj; p++) {
+
+		for (int y = 0; y < nDetYMap; y++)
+			pProjt[(p*nDetYMap *nDetXMap) + y] = 0;
+		
+		for (int x = 1; x < nDetXMap; x++)
+			pProjt[(p*nDetYMap *nDetXMap) + (x*nDetYMap)] = 0;
+	}
+		
+
+	// Integrate on I direction
+	for (int p = 0; p < nProj; p++) 
+		#pragma omp parallel for num_threads(nThreads) schedule(static)
+		for (int x = 0; x < nDetX; x++){
+			double sum = 0;
+			for (int y = 0; y < nDetY; y++){
+				sum += pProj[(p*nDetY *nDetX) + (x*nDetY) + y];
+				pProjt[(p*nDetYMap *nDetXMap) + ((x+1)*nDetYMap) + y + 1] = sum;
+			}
+		}
+	
+	// Integrate on J direction
+	for (int p = 0; p < nProj; p++) 	
+		#pragma omp parallel for num_threads(nThreads) schedule(static)
+		for (int y = 1; y < nDetYMap; y++) 
+			for (int x = 2; x < nDetXMap; x++) 
+				pProjt[(p*nDetYMap *nDetXMap) + (x*nDetYMap) + y] += pProjt[(p*nDetYMap *nDetXMap) + ((x - 1)*nDetYMap) + y];
+
+	
+	double* pDetmX_tmp = pDetmX + (nDetYMap * (nDetXMap - 2));
+
+	// For each projection
+	for (int p = 0; p < nProj; p++) {
+		
+		// Get specif tube angle for the projection
+		double theta = pTubeAngle[p] * M_PI / 180.0;
+
+		// Get specif detector angle for the projection
+		double phi = pDetAngle[p] * M_PI / 180.0;
+
+		//mexPrintf("Tube angle:%f Det angle:%f\n", theta, phi);
+
+		// Tube rotation
+		double rtubeY = ((tubeY - isoY)*cos(theta) - (tubeZ - isoZ)*sin(theta)) + isoY;
+		double rtubeZ = ((tubeY - isoY)*sin(theta) + (tubeZ - isoZ)*cos(theta)) + isoZ;
+
+		//mexPrintf("R tube Y:%f R tube Z:%f\n", rtubeY, rtubeZ);
+
+		// Detector rotation
+		for (int y = 0; y < nDetYMap; y++) {
+			pRdetY[y] = ((pDetY[y] - isoY)*cos(phi) - (pDetZ[y] - isoZ)*sin(phi)) + isoY;
+			pRdetZ[y] = ((pDetY[y] - isoY)*sin(phi) + (pDetZ[y] - isoZ)*cos(phi)) + isoZ;
+		}
+
+
+		// For each slice
+		for (int nz = 0; nz < nSlices; nz++) {
+			
+			/*
+
+			Map detector onto XY plane(Inside proj loop in case detector rotates)
+
+			*** Note: Matlab has linear indexing as a column of elements, i.e, the elements are actually stored in memory as queued columns.
+	
+			*/
+
+			// Map slice onto XY plane
+			mapDet2Slice(pDetmX, pDetmY, tubeX, rtubeY, rtubeZ, pDetX, pRdetY, pRdetZ, pObjZ[nz], nDetXMap, nDetYMap);
+
+			/*
+			S.2. Interpolation - Liu et al (2017)
+			*/
+								  
+			bilinear_interpolation(pSliceI, pProjt, pObjX, pObjY, pDetmX_tmp, pDetmY, nPixXMap, nPixYMap, nDetXMap, nDetYMap, nDetX, nDetY, p);
+
+			/*
+			S.3. Differentiation - Eq. 24 - Liu et al (2017)
+			*/
+						  
+			differentiation(pVolumet, pSliceI, tubeX, rtubeY, rtubeZ, pObjX, pObjY, pObjZ, nPixX, nPixY, nPixXMap, nPixYMap, du, dv, dx, dy, dz, nz);
+
+		} // Loop end slices
+
+	} // Loop end Projections
+
+	
+	// Copy pProjt to pProj
+	for (int nz = 0; nz < nSlices; nz++)
+		for (int x = 0; x < nPixX; x++)
+			for (int y = 0; y < nPixY; y++)
+				pVolume[(nz*nPixX*nPixY) + (x*nPixY) + y] = pVolumet[(nz*nPixX*nPixY) + (x*nPixY) + y] / (double) nProj;
+	
+	
+	// Interp
+	//for (int x = 0; x < nDetXMap; x++)
+	//	for (int y = 0; y < nDetYMap; y++) {
+	//		pProj[(0 * nDetXMap*nDetYMap) + (x*nDetYMap) + y] = projI[(x*nDetYMap) + y];
+	//	}
+
+	// Volume			
+	//for (int p = 0; p < nSlices; p++)
+	//	for (int x = 0; x < nPixXMap; x++)
+	//		for (int y = 0; y < nPixYMap+1; y++) {
+	//			pProj[(p * nPixXMap*nPixYMap) + (x*nPixYMap) + y] = pVolumet[(p * nPixXMap*nPixYMap) + (x*nPixYMap) + y];
+	//		}
+	
+	mxFree(pProjt);
+	mxFree(pSliceI);
+	mxFree(pVolumet);
+	mxFree(pDetX);
+	mxFree(pDetY);
+	mxFree(pDetZ);
+	mxFree(pObjX);
+	mxFree(pObjY);
+	mxFree(pObjZ);
+	mxFree(pDetmY);
+	mxFree(pDetmX);
+	mxFree(pRdetY);
+	mxFree(pRdetZ);
+	
+	time = clock() - time;
+	mexPrintf("Processing time: %f seconds.\n", ((double)time) / CLOCKS_PER_SEC);
+
+	return;	
+}
+
+
+// Make boundaries of detector and slices
+void mapBoudaries(double* pBound, 
+	const int nElem, 
+	const double valueLeftBound, 
+	const double sizeElem, 
+	const double offset){
+
+	for (int k = 0; k < nElem; k++)
+		pBound[k] = (k - valueLeftBound) * sizeElem + offset;
+
+	return;
+}
+
+// Map on XY plane
+void mapDet2Slice(double* const pXmapp,
+	double* const pYmapp,
+	double tubeX,
+	double tubeY,
+	double tubeZ,
+	double * const pXcoord,
+	double * const pYcoord,
+	double * const pZcoord,
+	double ZSlicecoord,
+	const int nXelem,
+	const int nYelem){
+
+	#pragma omp parallel num_threads(nThreads) 
+	{
+		int ind;
+		#pragma omp for schedule(static)
+			for (int x = 0; x < nXelem; x++)
+				for (int y = 0; y < nYelem; y++) {
+
+					ind = (x*nYelem) + y;
+					pXmapp[ind] = ((pXcoord[x] - tubeX)*(ZSlicecoord - pZcoord[y]) - (pXcoord[x] * tubeZ) + (pXcoord[x] * pZcoord[y])) / (-tubeZ + pZcoord[y]);
+
+					if (x == 0)
+						pYmapp[y] = ((pYcoord[y] - tubeY)*(ZSlicecoord - pZcoord[y]) - (pYcoord[y] * tubeZ) + (pYcoord[y] * pZcoord[y])) / (-tubeZ + pZcoord[y]);
+				}
+	}
+
+	return;
+}
+
+// Bilinear interpolation
+void bilinear_interpolation(double* pSliceI,
+	double* pProj,
+	double* pObjX,
+	double* pObjY,
+	double* pDetmX,
+	double* pDetmY,
+	const int nPixXMap,
+	const int nPixYMap,
+	const int nDetXMap,
+	const int nDetYMap,
+	const int nDetX,
+	const int nDetY,
+	const unsigned int np){
+
+	/*
+
+	S.2. Interpolation - Liu et al (2017)
+
+	Reference:
+	- https://en.wikipedia.org/wiki/Bilinear_interpolation
+	- https://stackoverflow.com/questions/21128731/bilinear-interpolation-in-c-c-and-cuda
+
+	Note: *** We are using the Unit Square equation ***
+
+	alpha = X - X1
+	1-alpha = X2 - X
+
+	beta = Y - Y1
+	1-beta = Y2 - Y
+
+	----> Xcoord 
+	|				0___________
+	v				|_d00_|_d10_|
+	Ycoord			|_d01_|_d11_|
+
+	Matlab code --
+	objX = nDetX - (objX ./ detmX(1, end - 1));
+	objY = (objY ./ detmX(1, end - 1)) - (detmY(1) / detmX(1, end - 1));
+
+	*/
+
+	#pragma omp parallel for num_threads(nThreads) schedule(static) 
+	for (int x = 0; x < nPixXMap; x++)
+		for (int y = 0; y < nPixYMap; y++) {
+
+			// Adjust the mapped coordinates to cross the range of (0-nDetX).*duMap 
+			//  Divide by pixelSize to get a unitary pixel size
+			const double xNormData = nDetX - pObjX[x] / pDetmX[0];
+			const signed int xData = (signed int) floor(xNormData);
+			const double  alpha = xNormData - xData;
+
+			// Adjust the mapped coordinates to cross the range of (0-nDetY).*dyMap  
+			//  Divide by pixelSize to get a unitary pixel size
+			const double yNormData = (pObjY[y] / pDetmX[0]) - (pDetmY[0] / pDetmX[0]);
+			const signed int yData = (signed int) floor(yNormData);
+			const double  beta = yNormData - yData;
+
+			double d00, d01, d10, d11;
+			if (((xNormData) >= 0) && ((xNormData) <= nDetX) && ((yNormData) >= 0) && ((yNormData) <= nDetY))	d00 = pProj[(np*nDetYMap*nDetXMap) + (xData*nDetYMap + yData)];						else    d00 = 0.0;
+			if (((xData + 1) > 0) && ((xData + 1) <= nDetX) && ((yNormData) >= 0) && ((yNormData) <= nDetY))	d10 = pProj[(np*nDetYMap*nDetXMap) + ((xData + 1)*nDetYMap + yData)]; 				else    d10 = 0.0;
+			if (((xNormData) >= 0) && ((xNormData) <= nDetX) && ((yData + 1) > 0) && ((yData + 1) <= nDetY))	d01 = pProj[(np*nDetYMap*nDetXMap) + (xData*nDetYMap + yData + 1)]; 				else    d01 = 0.0;
+			if (((xData + 1) > 0) && ((xData + 1) <= nDetX) && ((yData + 1) > 0) && ((yData + 1) <= nDetY))		d11 = pProj[(np*nDetYMap*nDetXMap) + ((xData + 1)*nDetYMap + yData + 1)];			else    d11 = 0.0;
+
+			double result_temp1 = alpha * d10 + (-d00 * alpha + d00);
+			double result_temp2 = alpha * d11 + (-d01 * alpha + d01);
+
+			pSliceI[x * nPixYMap + y] = beta * result_temp2 + (-result_temp1 * beta + result_temp1);
+		}
+
+	return;
+}
+
+// Differentiation
+void differentiation(double* pVolume,
+	double* pSliceI,
+	double tubeX, 
+	double rtubeY, 
+	double rtubeZ,
+	double* const pObjX, 
+	double* const pObjY, 
+	double* const pObjZ, 
+	const int nPixX,
+	const int nPixY,
+	const int nPixXMap,
+	const int nPixYMap,
+	const double du, 
+	const double dv, 
+	const double dx, 
+	const double dy, 
+	const double dz, 
+	const unsigned int nz){
+
+	/*
+
+	S.3. Differentiation - Eq. 24 - Liu et al (2017)
+
+	Slice integral projection
+	___________
+	|_A_|_B_|___|
+	|_C_|_D_|___|
+	|___|___|___|
+
+
+	(y,x)
+	________________
+	|_A_|__B__|_____|
+	|_C_|(0,0)|(0,1)|
+	|___|(1,0)|(1,1)|
+
+
+	Coordinates on intergal slice:
+
+	A = x * nPixYMap + y
+	B = ((x+1) * nPixYMap) + y
+	C = x * nPixYMap + y + 1
+	D = ((x+1) * nPixYMap) + y + 1
+
+	*/
+
+	#pragma omp parallel num_threads(nThreads)
+	{
+		unsigned int coordA;
+		unsigned int coordB;
+		unsigned int coordC;
+		unsigned int coordD;
+
+		double detMapSizeX;
+		double detMapSizeY;
+		double gamma;
+		double alpha;
+
+		double dA, dB, dC, dD;
+
+		#pragma omp for schedule(static)
+		for (int x = 0; x < nPixX; x++)
+			for (int y = 0; y < nPixY; y++) {
+
+				coordA = x * nPixYMap + y;
+				coordB = ((x + 1) * nPixYMap) + y;
+				coordC = coordA + 1;
+				coordD = coordB + 1;
+
+				// x - ray angle in X coord
+				gamma = atan((pObjX[x] + (dx / 2) - tubeX) / (rtubeZ - pObjZ[nz]));
+				
+				// x - ray angle in Y coord
+				alpha = atan((pObjY[y] + (dy / 2) - rtubeY) / (rtubeZ - pObjZ[nz]));
+				
+
+				dA = pSliceI[coordA];
+				dB = pSliceI[coordB];
+				dC = pSliceI[coordC];
+				dD = pSliceI[coordD];
+
+				// Treat border of interpolated integral detector
+				if (dC == 0 && dD == 0) {
+					dC = dA;
+					dD = dB;
+				}
+
+
+				// S.3.Differentiation - Eq. 24 - Liu et al(2017)
+				pVolume[(nPixX*nPixY*nz) + (x * nPixY) + y] += ((dD - dC - dB + dA)*(du*dv*dz / (cos(alpha)*cos(gamma)*dx*dy)));
+			}
+	}
+	return;
+}

+ 114 - 0
Functions/Sources/backprojectionDDb_mex/backprojectionDDb_mex.h

@@ -0,0 +1,114 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: March, 2019
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This is the header function
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Back-projection Branchless Distance Driven Code (CPU-Multithread)
+*/
+
+//typedef double user_dataType;
+//typedef float user_dataType;
+//#define user_mexdataType mxDOUBLE_CLASS
+//#define user_mexdataType mxSINGLE_CLASS
+
+#include "mex.h"
+#include <omp.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <time.h>
+
+// Global variable
+int nThreads;
+
+void backprojectionDDb(double* const pVolume,
+	double* const pProj,
+	double* const pTubeAngle,
+	double* const pDetAngle,
+	const  int nProj,
+	const  int nPixX,
+	const  int nPixY,
+	const  int nSlices,
+	const  int nDetX,
+	const  int nDetY,
+	const double dx,
+	const double dy,
+	const double dz,
+	const double du,
+	const double dv,
+	const double DSD,
+	const double DDR,
+	const double DAG);
+
+void mapBoudaries(double* pBound,
+	const int nElem,
+	const double valueLeftBound,
+	const double sizeElem,
+	const double offset);
+
+void mapDet2Slice(double* const pXmapp,
+	double* const pYmapp,
+	double tubeX,
+	double tubeY,
+	double tubeZ,
+	double * const pXcoord,
+	double * const pYcoord,
+	double * const pZcoord,
+	double ZSlicecoord,
+	const int nXelem,
+	const int nYelem);
+
+void bilinear_interpolation(double* pSliceI,
+	double* pProj,
+	double* pObjX,
+	double* pObjY,
+	double* pDetmX,
+	double* pDetmY,
+	const int nPixXMap,
+	const int nPixYMap,
+	const int nDetXMap,
+	const int nDetYMap,
+	const int nDetX,
+	const int nDetY,
+	const unsigned int np);
+
+void differentiation(double* pVolume,
+	double* pSliceI,
+	double tubeX,
+	double rtubeY,
+	double rtubeZ,
+	double* const pObjX,
+	double* const pObjY,
+	double* const pObjZ,
+	const int nPixX,
+	const int nPixY,
+	const int nPixXMap,
+	const int nPixYMap,
+	const double du,
+	const double dv,
+	const double dx,
+	const double dy,
+	const double dz,
+	const unsigned int nz);

+ 31 - 0
Functions/Sources/backprojectionDDb_mex/backprojectionDDb_mex.sln

@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29025.244
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "backprojectionDDb_mex", "backprojectionDDb_mex.vcxproj", "{2AF26169-F564-4738-BF85-4CB8507DE368}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{2AF26169-F564-4738-BF85-4CB8507DE368}.Debug|x64.ActiveCfg = Debug|x64
+		{2AF26169-F564-4738-BF85-4CB8507DE368}.Debug|x64.Build.0 = Debug|x64
+		{2AF26169-F564-4738-BF85-4CB8507DE368}.Debug|x86.ActiveCfg = Debug|Win32
+		{2AF26169-F564-4738-BF85-4CB8507DE368}.Debug|x86.Build.0 = Debug|Win32
+		{2AF26169-F564-4738-BF85-4CB8507DE368}.Release|x64.ActiveCfg = Release|x64
+		{2AF26169-F564-4738-BF85-4CB8507DE368}.Release|x64.Build.0 = Release|x64
+		{2AF26169-F564-4738-BF85-4CB8507DE368}.Release|x86.ActiveCfg = Release|Win32
+		{2AF26169-F564-4738-BF85-4CB8507DE368}.Release|x86.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {6F1FE977-0114-4992-ABF9-D44CB2F3202B}
+	EndGlobalSection
+EndGlobal

+ 151 - 0
Functions/Sources/backprojectionDDb_mex/backprojectionDDb_mex.vcxproj

@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="backprojectionDDb_mex.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="backprojectionDDb_mex.h" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <ProjectGuid>{2af26169-f564-4738-bf85-4cb8507de368}</ProjectGuid>
+    <RootNamespace>backprojectionDDb_mex</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <TargetExt>.mexw64</TargetExt>
+    <IncludePath>C:\Program Files\MATLAB\R2017b\extern\include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <TargetExt>.mexw64</TargetExt>
+    <IncludePath>C:\Program Files\MATLAB\R2017b\extern\include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+      <OpenMPSupport>true</OpenMPSupport>
+      <AdditionalOptions>/Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libut.lib;libmx.lib;libmex.lib;libmat.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalOptions>/export:mexFunction %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+      <OpenMPSupport>true</OpenMPSupport>
+      <AdditionalOptions>/Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libut.lib;libmx.lib;libmex.lib;libmat.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalOptions>/export:mexFunction %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 25 - 0
Functions/Sources/backprojectionDDb_mex_CUDA/backprojectionDDb_mex_CUDA.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29025.244
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "backprojectionDDb_mex_CUDA", "backprojectionDDb_mex_CUDA.vcxproj", "{791A9536-358C-4597-AE7C-D58CD1C6FA0D}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x64 = Debug|x64
+		Release|x64 = Release|x64
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{791A9536-358C-4597-AE7C-D58CD1C6FA0D}.Debug|x64.ActiveCfg = Debug|x64
+		{791A9536-358C-4597-AE7C-D58CD1C6FA0D}.Debug|x64.Build.0 = Debug|x64
+		{791A9536-358C-4597-AE7C-D58CD1C6FA0D}.Release|x64.ActiveCfg = Release|x64
+		{791A9536-358C-4597-AE7C-D58CD1C6FA0D}.Release|x64.Build.0 = Release|x64
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {38ADF92A-D6C7-4810-A45F-C315C49569AA}
+	EndGlobalSection
+EndGlobal

+ 103 - 0
Functions/Sources/backprojectionDDb_mex_CUDA/backprojectionDDb_mex_CUDA.vcxproj

@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="backprojectionDDb_mex_cuda.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="backprojectionDDb_mex_cuda.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <CudaCompile Include="kernel.cu" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{791a9536-358c-4597-ae7c-d58cd1c6fa0d}</ProjectGuid>
+    <RootNamespace>backprojectionDDb_mex_CUDA</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v142</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v142</PlatformToolset>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+    <Import Project="$(VCTargetsPath)\BuildCustomizations\CUDA 10.1.props" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <TargetExt>.mexw64</TargetExt>
+    <IncludePath>C:\ProgramData\NVIDIA Corporation\CUDA Samples\v10.1\common\inc;C:\Program Files\MATLAB\R2017b\extern\include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <TargetExt>.mexw64</TargetExt>
+    <IncludePath>C:\ProgramData\NVIDIA Corporation\CUDA Samples\v10.1\common\inc;C:\Program Files\MATLAB\R2017b\extern\include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>libut.lib;libmx.lib;libmex.lib;libmat.lib;cudart_static.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalOptions>/export:mexFunction %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+    <CudaCompile>
+      <TargetMachinePlatform>64</TargetMachinePlatform>
+    </CudaCompile>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>libut.lib;libmx.lib;libmex.lib;libmat.lib;cudart_static.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalOptions>/export:mexFunction %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+    <CudaCompile>
+      <TargetMachinePlatform>64</TargetMachinePlatform>
+    </CudaCompile>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+    <Import Project="$(VCTargetsPath)\BuildCustomizations\CUDA 10.1.targets" />
+  </ImportGroup>
+</Project>

+ 165 - 0
Functions/Sources/backprojectionDDb_mex_CUDA/backprojectionDDb_mex_cuda.cpp

@@ -0,0 +1,165 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: March, 2019
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 backprojectionDDb_mex_CUDA(proj,param)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function reconstruct the 3D volume from projections, based on
+%     the Distance-Driven principle. It works by calculating the overlap
+%     in X and Y axis of the volume and the detector boundaries.
+%     The geometry is for DBT with half cone-beam. All parameters are set
+%     in "ParameterSettings" code.
+%
+%     INPUT:
+%
+%     - proj = 2D projections for each angle
+%     - param = Parameter of all geometry
+%	  - nProj = projection number to be backprojected
+%
+%     OUTPUT:
+%
+%     - data3d = reconstructed volume.
+%
+%     Reference:
+%     - Branchless Distance Driven Projection and Backprojection,
+%     Samit Basu and Bruno De Man (2006)
+%     - GPU Acceleration of Branchless Distance Driven Projection and
+%     Backprojection, Liu et al (2016)
+%     - GPU-Based Branchless Distance-Driven Projection and Backprojection,
+%     Liu et al (2017)
+%     - A GPU Implementation of Distance-Driven Computed Tomography,
+%     Ryan D. Wagner (2017)
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Distance Driven Back-projection Code
+*/
+
+#include "backprojectionDDb_mex_cuda.h"
+
+/****************************************************************************
+* function: mexFunction() - Matlab interface function.						*
+* INPUTS:																	*
+*   nlhs - Number of expected output mxArrays, specified as an integer.		*
+*   plhs[] - Array of pointers to the expected mxArray output arguments.	*
+*   nrhs - Number of input mxArrays, specified as an integer.				*
+*   prhs[] - Array of pointers to the mxArray input arguments.				*
+****************************************************************************/
+
+void mexFunction(int nlhs, mxArray *plhs[],
+	int nrhs, const mxArray *prhs[]) {
+
+
+	/* Check for proper number of arguments */
+	if (nrhs != 3) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:nargin",
+			"backprojection_mex requires three input arguments.");
+	}
+	if (nlhs != 1) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:nargout",
+			"backprojection_mex requires one output argument.");
+	}
+
+	/* Check if the input is of proper type */
+	if (!mxIsDouble(prhs[0])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument has to be double.");
+	}
+	if (!mxIsStruct(prhs[1])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"Second argument has to be a struct.");
+	}
+	if (!mxIsDouble(prhs[2])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"Third argument has to be double.");
+	}
+
+	double* const pProj = (double*)(mxGetPr(prhs[0])); // This variable has to come as single/double
+	double* const pTubeAngleD = mxGetPr(mxGetField(prhs[1], 0, "tubeDeg"));
+	double* const pDetAngleD = mxGetPr(mxGetField(prhs[1], 0, "detectorDeg"));
+
+
+	const int unsigned nProj = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nProj"));
+
+	const int unsigned nPixX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nx"));
+	const int unsigned nPixY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "ny"));
+	const int unsigned nSlices = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nz"));
+	const int unsigned nDetX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nu"));
+	const int unsigned nDetY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nv"));
+
+	const double dx = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dx"));
+	const double dy = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dy"));
+	const double dz = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dz"));
+	const double du = (const double)mxGetScalar(mxGetField(prhs[1], 0, "du"));
+	const double dv = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dv"));
+
+	const double DSD = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DSD"));
+	const double DDR = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DDR"));
+	const double DAG = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DAG"));
+
+	const double idXProj = (const double)mxGetScalar(prhs[2]);
+
+	mwSize NRow = mxGetM(prhs[0]);
+	mwSize NCol = mxGetN(prhs[0]);
+	mwSize NElemVol = mxGetNumberOfElements(prhs[0]);
+
+	/* Check if the input is in proper size */
+	if (NRow != nDetY) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument needs to have the same number of rows as in the configuration file.");
+	}
+
+	if (NCol / nProj != nDetX) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument needs to have the same number of columns as in the configuration file.");
+	}
+
+	if (idXProj >= nProj ){
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"Third argument needs to be less than the proj number (0->nproj-1)");
+	}
+
+	// Cast double vectors to single 
+	double* const pTubeAngle = (double*)mxMalloc(nProj * sizeof(double));
+	double* const pDetAngle = (double*)mxMalloc(nProj * sizeof(double));
+
+	for (unsigned int n = 0; n < nProj; n++) {
+		pTubeAngle[n] = (double)pTubeAngleD[n];
+		pDetAngle[n] = (double)pDetAngleD[n];
+	}
+
+	//mexPrintf("Nx:%d Ny:%d Nz:%d \nNu:%d Nv:%d \nDx:%.2f Dy:%.2f Dz:%.2f \nDu:%.2f Dv:%.2f \nDSD:%.2f DDR:%.2f \n", nPixX, nPixY, nSlices, nDetX, nDetY, dx, dy, dz, du, dv, DSD, DDR);
+
+
+	// Output memory allocation
+	mwSize dims[3] = { nPixY,nPixX,nSlices };
+
+	plhs[0] = mxCreateNumericArray(3, dims, mxDOUBLE_CLASS, mxREAL);
+	double* const pVolume = (double*)mxGetData(plhs[0]);
+
+	backprojectionDDb(pVolume, pProj, pTubeAngle, pDetAngle, idXProj, nProj, nPixX, nPixY, nSlices, nDetX, nDetY, dx, dy, dz, du, dv, DSD, DDR, DAG);
+
+	mxFree(pTubeAngle);
+	mxFree(pDetAngle);
+
+	return;
+
+}

+ 69 - 0
Functions/Sources/backprojectionDDb_mex_CUDA/backprojectionDDb_mex_cuda.h

@@ -0,0 +1,69 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: March, 2019
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This is the header function
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Distance Driven Back-projection header
+*/
+
+//typedef double user_dataType;
+//typedef float user_dataType;
+//#define user_mexdataType mxDOUBLE_CLASS
+//#define user_mexdataType mxSINGLE_CLASS
+
+#include "mex.h"
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+// Includes CUDA
+#include <cuda_runtime.h>
+
+// Utilities and timing functions
+#include <helper_functions.h>    // includes cuda.h and cuda_runtime_api.h
+
+// CUDA helper functions
+#include <helper_cuda.h>         // helper functions for CUDA error check
+
+#define integrateXcoord 1
+#define integrateYcoord 0
+
+
+/*
+Reference: TIGRE - https://github.com/CERN/TIGRE
+*/
+#define cudaCheckErrors(msg) \
+do { \
+        cudaError_t __err = cudaGetLastError(); \
+        if (__err != cudaSuccess) { \
+                mexPrintf("%s \n",msg);\
+                mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",cudaGetErrorString(__err));\
+        } \
+} while (0)
+
+
+void backprojectionDDb(double* const h_pVolume, double* const h_pProj, double* const h_pTubeAngle, double* const h_pDetAngle, const double idXProj, const unsigned int nProj, const unsigned int nPixX,
+	const unsigned int nPixY, const unsigned int nSlices, const unsigned int nDetX, const unsigned int nDetY, const double dx, const double dy, double dz, const double du,
+	const double dv, const double DSD, const double DDR, const double DAG);

+ 859 - 0
Functions/Sources/backprojectionDDb_mex_CUDA/kernel.cu

@@ -0,0 +1,859 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: March, 2019
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 backprojectionDDb_mex_CUDA(proj,param)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function reconstruct the 3D volume from projections, based on
+%     the Distance-Driven principle. It works by calculating the overlap
+%     in X and Y axis of the volume and the detector boundaries.
+%     The geometry is for DBT with half cone-beam. All parameters are set
+%     in "ParameterSettings" code.
+%
+%     INPUT:
+%
+%     - proj = 2D projections for each angle
+%     - param = Parameter of all geometry
+%	  - nProj = projection number to be backprojected
+%
+%     OUTPUT:
+%
+%     - data3d = reconstructed volume.
+%
+%     Reference:
+%     - Branchless Distance Driven Projection and Backprojection,
+%     Samit Basu and Bruno De Man (2006)
+%     - GPU Acceleration of Branchless Distance Driven Projection and
+%     Backprojection, Liu et al (2016)
+%     - GPU-Based Branchless Distance-Driven Projection and Backprojection,
+%     Liu et al (2017)
+%     - A GPU Implementation of Distance-Driven Computed Tomography,
+%     Ryan D. Wagner (2017)
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Distance Driven Back-projection Code
+*/
+
+#include "backprojectionDDb_mex_cuda.h"
+
+
+/****************************************************************************
+*							CUDA Kernels									*
+****************************************************************************/
+__global__ void pad_projections_kernel(double* d_img, const int nDetXMap, const int nDetYMap, const int nElem, unsigned int np) {
+
+	const int threadGId = blockIdx.x * blockDim.x + threadIdx.x;
+
+	// Make sure we don't try and access memory outside
+	// by having any threads mapped there return early
+	if (threadGId >= nElem)
+		return;
+
+	d_img[(np*nDetYMap *nDetXMap) + (threadGId*nDetYMap)] = 0;
+
+	return;
+}
+
+__global__ void map_boudaries_kernel(double* d_pBound, const int nElem, const double valueLeftBound, const double sizeElem, const double offset) {
+
+	const int threadGId = blockIdx.x * blockDim.x + threadIdx.x;
+
+	// Make sure we don't try and access memory outside
+	// by having any threads mapped there return early
+	if (threadGId >= nElem)
+		return;
+
+	d_pBound[threadGId] = (threadGId - valueLeftBound) * sizeElem + offset;
+
+	return;
+}
+
+__global__ void rot_detector_kernel(double* d_pRdetY, double* d_pRdetZ, double* d_pYcoord, double* d_pZcoord, const double yOffset, const double zOffset,
+	const double phi, const int nElem) {
+
+	const int threadGId = blockIdx.x * blockDim.x + threadIdx.x;
+
+	// Make sure we don't try and access memory outside
+	// by having any threads mapped there return early
+	if (threadGId >= nElem)
+		return;
+
+	// cos and sin are in measured in radians.
+
+	d_pRdetY[threadGId] = ((d_pYcoord[threadGId] - yOffset)* cos(phi) - (d_pZcoord[threadGId] - zOffset)* sin(phi)) + yOffset;
+	d_pRdetZ[threadGId] = ((d_pYcoord[threadGId] - yOffset)* sin(phi) + (d_pZcoord[threadGId] - zOffset)* cos(phi)) + zOffset;
+
+	return;
+
+}
+
+__global__ void mapDet2Slice_kernel(double* const pXmapp, double* const pYmapp, double tubeX, double tubeY, double tubeZ, double* const pXcoord,
+	double* const pYcoord, double* const pZcoord, double* const pZSlicecoord, const int nDetXMap, const int nDetYMap, const unsigned int nz) {
+
+	/*
+
+	Note: Matlab has linear indexing as a column of elements, i.e, the elements are actually stored in memory as queued columns.
+	So threads in X are launched in the column direction (DBT Y coord), and threads in Y are launched in the row direction (DBT X coord).
+
+	*/
+
+	const int2 thread_2D_pos = make_int2(blockIdx.x * blockDim.x + threadIdx.x,
+		blockIdx.y * blockDim.y + threadIdx.y);
+
+	const int thread_1D_pos = thread_2D_pos.y * nDetYMap + thread_2D_pos.x;
+
+	// Make sure we don't try and access memory outside the detector
+	// by having any threads mapped there return early
+	if (thread_2D_pos.x >= nDetYMap || thread_2D_pos.y >= nDetXMap)
+		return;
+
+	pXmapp[thread_1D_pos] = ((pXcoord[thread_2D_pos.y] - tubeX)*(pZSlicecoord[nz] - pZcoord[thread_2D_pos.x]) - (pXcoord[thread_2D_pos.y] * tubeZ) + (pXcoord[thread_2D_pos.y] * pZcoord[thread_2D_pos.x])) / (-tubeZ + pZcoord[thread_2D_pos.x]);
+
+	if (thread_2D_pos.y == 0)
+		pYmapp[thread_2D_pos.x] = ((pYcoord[thread_2D_pos.x] - tubeY)*(pZSlicecoord[nz] - pZcoord[thread_2D_pos.x]) - (pYcoord[thread_2D_pos.x] * tubeZ) + (pYcoord[thread_2D_pos.x] * pZcoord[thread_2D_pos.x])) / (-tubeZ + pZcoord[thread_2D_pos.x]);
+
+	return;
+}
+
+__global__ void img_integration_kernel(double* d_img, const int nPixX, const int nPixY, bool direction, unsigned int offsetX, unsigned int offsetY, unsigned int nSlices) {
+
+	/*
+
+	Integration of 2D slices over the whole volume
+
+	(S.1.Integration. - Liu et al(2017))
+
+	** Perfom an inclusive scan **
+
+	*/
+
+	const int3 memory_2D_pos = make_int3(blockIdx.x * blockDim.x + threadIdx.x + offsetX,
+		blockIdx.y * blockDim.y + threadIdx.y + offsetY,
+		blockIdx.z * blockDim.z + threadIdx.z);
+
+	const int2 thread_2D_pos = make_int2(blockIdx.x * blockDim.x + threadIdx.x,
+		blockIdx.y * blockDim.y + threadIdx.y);
+
+
+	// Make sure we don't try and access memory outside the detector
+	// by having any threads mapped there return early
+	if (memory_2D_pos.x >= nPixY || memory_2D_pos.y >= nPixX || memory_2D_pos.z >= nSlices)
+		return;
+
+
+	if (direction == integrateXcoord) {
+
+		for (int s = 1; s <= blockDim.y; s *= 2) {
+
+			int spot = thread_2D_pos.y - s;
+
+			double val = 0;
+
+			if (spot >= 0) {
+				val = d_img[(memory_2D_pos.z*nPixY*nPixX) + (offsetY + spot) * nPixY + memory_2D_pos.x];
+			}
+			__syncthreads();
+
+			if (spot >= 0) {
+				d_img[(memory_2D_pos.z*nPixY*nPixX) + (memory_2D_pos.y * nPixY) + memory_2D_pos.x] += val;
+			}
+			__syncthreads();
+		}
+	}
+	else
+	{
+
+		for (int s = 1; s <= blockDim.x; s *= 2) {
+
+			int spot = thread_2D_pos.x - s;
+
+			double val = 0;
+
+			if (spot >= 0) {
+				val = d_img[(memory_2D_pos.z*nPixY*nPixX) + memory_2D_pos.y * nPixY + spot + offsetX];
+			}
+			__syncthreads();
+
+			if (spot >= 0) {
+				d_img[(memory_2D_pos.z*nPixY*nPixX) + (memory_2D_pos.y * nPixY) + memory_2D_pos.x] += val;
+			}
+			__syncthreads();
+		}
+
+	}
+	return;
+}
+
+//__global__ void bilinear_interp_kernel(double* d_interpData, cudaTextureObject_t texObj, double* const d_pDetmX, double* const d_pDetmY, const int nDetXMap, const int nDetYMap, const double2 normFactor, const double2 offset)
+//{
+//
+//	const int2 thread_2D_pos = make_int2(blockIdx.x * blockDim.x + threadIdx.x,
+//										 blockIdx.y * blockDim.y + threadIdx.y);
+//
+//	// Make sure we don't try and access memory outside the detector
+//	// by having any threads mapped there return early
+//	if (thread_2D_pos.x >= nDetYMap || thread_2D_pos.y >= nDetXMap)
+//		return;
+//
+//	// Normalize coordinates from 0-1 (coords on texture memory)
+//	double xQuery = d_pDetmX[thread_2D_pos.y * nDetYMap + thread_2D_pos.x] / normFactor.x + offset.x + (-.5f * (1 / normFactor.x));
+//	double yQuery = d_pDetmY[thread_2D_pos.x] / normFactor.y + offset.y + (.5f * (1 / normFactor.y));
+//
+//	// Read from texture and write to global memory
+//	d_interpData[thread_2D_pos.y * nDetYMap + thread_2D_pos.x] = tex2D<double>(texObj, xQuery, yQuery);
+//
+//	return;
+//}
+
+__global__ void bilinear_interpolation_kernel_GPU(double* d_sliceI, double* d_pProj, double* d_pObjX, double* d_pObjY, double* d_pDetmX, double* d_pDetmY, const int nPixXMap, const int nPixYMap,
+	const int nDetXMap, const int nDetYMap, const int nDetX, const int nDetY, const unsigned int np) {
+
+	const int2 thread_2D_pos = make_int2(blockIdx.x * blockDim.x + threadIdx.x,
+										 blockIdx.y * blockDim.y + threadIdx.y);
+
+	// Make sure we don't try and access memory outside the detector
+	// by having any threads mapped there return early
+	if (thread_2D_pos.x >= nPixYMap || thread_2D_pos.y >= nPixXMap)
+		return;
+
+	/*
+
+	S.2. Interpolation - Liu et al (2017)
+
+	Reference:
+	- https://en.wikipedia.org/wiki/Bilinear_interpolation
+	- https://stackoverflow.com/questions/21128731/bilinear-interpolation-in-c-c-and-cuda
+
+	Note: *** We are using the Unit Square equation ***
+
+	alpha = X - X1
+	1-alpha = X2 - X
+
+	beta = Y - Y1
+	1-beta = Y2 - Y
+
+	----> Xcoord (thread_2D_pos.y)
+	|							0___________
+	v							|_d00_|_d10_|
+	Ycoord (thread_2D_pos.x)	|_d01_|_d11_|
+
+	Matlab code --
+	objX = nDetX - (objX ./ detmX(1, end - 1));
+	objY = (objY ./ detmX(1, end - 1)) - (detmY(1) / detmX(1, end - 1));
+
+	*/
+
+	// Adjust the mapped coordinates to cross the range of (0-nDetX).*duMap 
+	//  Divide by pixelSize to get a unitary pixel size
+	const double xNormData = nDetX - d_pObjX[thread_2D_pos.y] / d_pDetmX[0];
+	const signed int    xData = floor(xNormData);
+	const double  alpha = xNormData - xData;
+
+	// Adjust the mapped coordinates to cross the range of (0-nDetY).*dyMap  
+	//  Divide by pixelSize to get a unitary pixel size
+	const double yNormData = (d_pObjY[thread_2D_pos.x] / d_pDetmX[0]) - (d_pDetmY[0] / d_pDetmX[0]);
+	const signed int    yData = floor(yNormData);
+	const double  beta = yNormData - yData;
+
+	double d00, d01, d10, d11;
+	if (((xNormData) >= 0) && ((xNormData) <= nDetX) && ((yNormData) >= 0) && ((yNormData) <= nDetY))	d00 = d_pProj[(np*nDetYMap*nDetXMap) + (xData*nDetYMap + yData)];						else    d00 = 0.0;
+	if (((xData + 1) > 0) && ((xData + 1) <= nDetX) && ((yNormData) >= 0) && ((yNormData) <= nDetY))	d10 = d_pProj[(np*nDetYMap*nDetXMap) + ((xData + 1)*nDetYMap + yData)]; 				else    d10 = 0.0;
+	if (((xNormData) >= 0) && ((xNormData) <= nDetX) && ((yData + 1) > 0) && ((yData + 1) <= nDetY))	d01 = d_pProj[(np*nDetYMap*nDetXMap) + (xData*nDetYMap + yData + 1)]; 				else    d01 = 0.0;
+	if (((xData + 1) > 0) && ((xData + 1) <= nDetX) && ((yData + 1) > 0) && ((yData + 1) <= nDetY))		d11 = d_pProj[(np*nDetYMap*nDetXMap) + ((xData + 1)*nDetYMap + yData + 1)];			else    d11 = 0.0;
+
+	double result_temp1 = alpha * d10 + (-d00 * alpha + d00);
+	double result_temp2 = alpha * d11 + (-d01 * alpha + d01);
+
+	d_sliceI[thread_2D_pos.y * nPixYMap + thread_2D_pos.x] = beta * result_temp2 + (-result_temp1 * beta + result_temp1);
+
+}
+
+__global__ void differentiation_kernel(double* d_pVolume, double* d_sliceI, double tubeX, double rtubeY, double rtubeZ,
+	double* const d_pObjX, double* const d_pObjY, double* const d_pObjZ, const int nPixX, const int nPixY, const int nPixXMap,
+	const int nPixYMap, const double du, const double dv, const double dx, const double dy, const double dz, const unsigned int nz) {
+
+	const int2 thread_2D_pos = make_int2(blockIdx.x * blockDim.x + threadIdx.x,
+		blockIdx.y * blockDim.y + threadIdx.y);
+
+	const int thread_1D_pos = (nPixX*nPixY*nz) + (thread_2D_pos.y * nPixY) + thread_2D_pos.x;
+
+	// Make sure we don't try and access memory outside the detector
+	// by having any threads mapped there return early
+	if (thread_2D_pos.x >= nPixY || thread_2D_pos.y >= nPixX)
+		return;
+
+	/*
+
+	S.3. Differentiation - Eq. 24 - Liu et al (2017)
+
+	Detector integral projection
+	___________
+	|_A_|_B_|___|
+	|_C_|_D_|___|
+	|___|___|___|
+
+
+	(thread_2D_pos.x,thread_2D_pos.y)
+	________________
+	|_A_|__B__|_____|
+	|_C_|(0,0)|(0,1)|
+	|___|(1,0)|(1,1)|
+
+	Threads are lauched from D up to nPixX (thread_2D_pos.y) and nPixY (thread_2D_pos.x)
+	i.e., they are running on the detector image. Thread (0,0) is on D.
+
+	Coordinates on intergal projection:
+
+	A = thread_2D_pos.y * nPixYMap + thread_2D_pos.x
+	B = ((thread_2D_pos.y+1) * nPixYMap) + thread_2D_pos.x
+	C = thread_2D_pos.y * nPixYMap + thread_2D_pos.x + 1
+	D = ((thread_2D_pos.y+1) * nPixYMap) + thread_2D_pos.x + 1
+
+	*/
+
+	unsigned int coordA = thread_2D_pos.y * nPixYMap + thread_2D_pos.x;
+	unsigned int coordB = ((thread_2D_pos.y + 1) * nPixYMap) + thread_2D_pos.x;
+	unsigned int coordC = coordA + 1;
+	unsigned int coordD = coordB + 1;
+
+	// x - ray angle in X coord
+	double gamma = atan((d_pObjX[thread_2D_pos.y] + (dx / 2.0) - tubeX) / (rtubeZ - d_pObjZ[nz]));
+
+	// x - ray angle in Y coord
+	double alpha = atan((d_pObjY[thread_2D_pos.x] + (dy / 2.0) - rtubeY) / (rtubeZ - d_pObjZ[nz]));
+
+
+	double dA, dB, dC, dD;
+
+	dA = d_sliceI[coordA];
+	dB = d_sliceI[coordB];
+	dC = d_sliceI[coordC];
+	dD = d_sliceI[coordD];
+
+	// Treat border of interpolated integral detector
+	if (dC == 0 && dD == 0) {
+		dC = dA;
+		dD = dB;
+	}
+
+
+	// S.3.Differentiation - Eq. 24 - Liu et al(2017)
+	d_pVolume[thread_1D_pos] += ((dD - dC - dB + dA)*(du*dv*dz / (cos(alpha)*cos(gamma)*dx*dy)));
+
+	return;
+}
+
+__global__ void division_kernel(double* d_img, const int nPixX, const int nPixY, unsigned int nSlices, unsigned int nProj){
+
+	const int3 thread_2D_pos = make_int3(blockIdx.x * blockDim.x + threadIdx.x,
+		blockIdx.y * blockDim.y + threadIdx.y,
+		blockIdx.z * blockDim.z + threadIdx.z);
+
+	const int thread_1D_pos = (nPixX*nPixY*thread_2D_pos.z) + (thread_2D_pos.y * nPixY) + thread_2D_pos.x;
+
+	// Make sure we don't try and access memory outside the detector
+	// by having any threads mapped there return early
+	if (thread_2D_pos.x >= nPixY || thread_2D_pos.y >= nPixX || thread_2D_pos.z >= nSlices)
+		return;
+
+	d_img[thread_1D_pos] /= (double) nProj;
+
+}
+
+
+/****************************************************************************
+* function: backprojectionDDb() - CUDA backprojection Branchless Distance Driven.	*
+****************************************************************************/
+
+void backprojectionDDb(double* const h_pVolume,
+	double* const h_pProj,
+	double* const h_pTubeAngle,
+	double* const h_pDetAngle,
+	const double idXProj,
+	const unsigned int nProj,
+	const unsigned int nPixX,
+	const unsigned int nPixY,
+	const unsigned int nSlices,
+	const unsigned int nDetX,
+	const unsigned int nDetY,
+	const double dx,
+	const double dy,
+	const double dz,
+	const double du,
+	const double dv,
+	const double DSD,
+	const double DDR,
+	const double DAG)
+{
+
+	// Unique GPU
+	int devID = 0;
+
+	cudaDeviceProp deviceProp;
+	cudaGetDeviceProperties(&deviceProp, devID);
+	cudaCheckErrors("cudaGetDeviceProperties");
+
+	const unsigned int maxThreadsPerBlock = deviceProp.maxThreadsPerBlock;
+
+
+	mexPrintf("GPU Device %d: \"%s\" with compute capability %d.%d has %d Multi-Processors and %zu bytes of global memory\n\n", devID,
+		deviceProp.name, deviceProp.major, deviceProp.minor, deviceProp.multiProcessorCount, deviceProp.totalGlobalMem);
+
+	
+	// Create timmer variables and start it
+	StopWatchInterface *timer = NULL;
+	sdkCreateTimer(&timer);
+	sdkStartTimer(&timer);
+	
+	//cudaStream_t stream1;
+	//cudaStreamCreate(&stream1);
+
+	dim3 threadsPerBlock(1, 1, 1);
+	dim3 blockSize(1, 1, 1);
+
+
+	// Number of mapped detector and pixels
+	const int nDetXMap = nDetX + 1;
+	const int nDetYMap = nDetY + 1;
+	const int nPixXMap = nPixX + 1;
+	const int nPixYMap = nPixY + 1;
+
+
+	// Convention: h_ variables live on host
+	// Convention: d_ variables live on device (GPU global mem)
+	double* d_pProj;
+	double* d_sliceI;
+	double* d_pVolume;
+	double* d_pTubeAngle;
+	double* d_pDetAngle;
+	
+	
+	// Allocate global memory on the device, place result in "d_----"
+	cudaMalloc((void **)&d_pProj, nDetXMap*nDetYMap*nProj * sizeof(double)); 
+	cudaMalloc((void **)&d_sliceI, nPixXMap*nPixYMap * sizeof(double));
+	cudaMalloc((void **)&d_pVolume, nPixX*nPixY*nSlices * sizeof(double));
+	cudaMalloc((void **)&d_pTubeAngle, nProj * sizeof(double));
+	cudaMalloc((void **)&d_pDetAngle, nProj * sizeof(double));
+	
+	cudaCheckErrors("cudaMalloc Initial");
+	
+	// Copy data from host memory "h_----" to device memory "d_----"
+	cudaMemcpy((void *)d_pTubeAngle, (void *)h_pTubeAngle, nProj * sizeof(double), cudaMemcpyHostToDevice);
+	cudaMemcpy((void *)d_pDetAngle, (void *)h_pDetAngle, nProj * sizeof(double), cudaMemcpyHostToDevice);
+
+	cudaCheckErrors("cudaMemcpy Angles");
+	
+
+	/*
+	Copy projection data from host memory "h_----" to device memory "d_----" padding with zeros for image integation
+	*/
+	
+
+	// Initialize first column and row with zeros
+	double* h_pProj_tmp;
+	double* d_pProj_tmp;
+
+	threadsPerBlock.x = maxThreadsPerBlock;
+	blockSize.x = (nDetXMap / maxThreadsPerBlock) + 1;
+
+	for (unsigned int np = 0; np < nProj; np++) {
+
+		// Pad on X coord direction
+		pad_projections_kernel << <blockSize, threadsPerBlock >> > (d_pProj, nDetXMap, nDetYMap, nDetXMap, np);
+
+		// Pad on Y coord direction
+		d_pProj_tmp = d_pProj + (nDetXMap*nDetYMap*np) + 1;
+		cudaMemset(d_pProj_tmp, 0, nPixY * sizeof(double));
+		cudaCheckErrors("cudaMemset Padding Projections");
+	}
+	
+	
+	// Copy projections data from host memory
+	for (unsigned int np = 0; np < nProj; np++)
+		for (unsigned int c = 0; c < nDetX; c++) {
+
+			h_pProj_tmp = h_pProj + (c *nDetY) + (nDetX*nDetY*np);
+			d_pProj_tmp = d_pProj + (((c + 1) *nDetYMap) + 1) + (nDetXMap*nDetYMap*np);
+
+			cudaMemcpy((void *)d_pProj_tmp, (void *)h_pProj_tmp, nDetY * sizeof(double), cudaMemcpyHostToDevice);
+			cudaCheckErrors("cudaMemcpy Projections");
+		}
+
+	
+	
+	// Pointer for projections coordinates
+	double* d_pDetX;
+	double* d_pDetY;
+	double* d_pDetZ;
+	double* d_pObjX;
+	double* d_pObjY;
+	double* d_pObjZ;
+
+	// Allocate global memory on the device for projections coordinates
+	cudaMalloc((void **)&d_pDetX, nDetXMap * sizeof(double));
+	cudaMalloc((void **)&d_pDetY, nDetYMap * sizeof(double));
+	cudaMalloc((void **)&d_pDetZ, nDetYMap * sizeof(double));
+	cudaMalloc((void **)&d_pObjX, nPixXMap * sizeof(double));
+	cudaMalloc((void **)&d_pObjY, nPixYMap * sizeof(double));
+	cudaMalloc((void **)&d_pObjZ, nSlices * sizeof(double));
+
+	cudaCheckErrors("cudaMalloc Coordinates");
+
+
+
+	// Pointer for mapped coordinates
+	double* d_pDetmY;
+	double* d_pDetmX;
+
+
+	// Allocate global memory on the device for mapped coordinates
+	cudaMalloc((void **)&d_pDetmY, nDetYMap * sizeof(double));
+	cudaMalloc((void **)&d_pDetmX, nDetYMap * nDetXMap * sizeof(double));
+
+	cudaCheckErrors("cudaMalloc Map-Coordinates");
+
+
+	// Pointer for rotated detector coords
+	double* d_pRdetY;
+	double* d_pRdetZ;
+
+	// Allocate global memory on the device for for rotated detector coords
+	cudaMalloc((void **)&d_pRdetY, nDetYMap * sizeof(double));
+	cudaMalloc((void **)&d_pRdetZ, nDetYMap * sizeof(double));
+
+	cudaCheckErrors("cudaMalloc Rot-Coordinates");
+	
+	// Generate detector and object boudaries
+	
+	threadsPerBlock.x = maxThreadsPerBlock;
+
+	blockSize.x = (nDetX / maxThreadsPerBlock) + 1;
+
+	map_boudaries_kernel << <blockSize, threadsPerBlock >> > (d_pDetX, nDetXMap, (double)nDetX, -du, 0.0);
+
+	blockSize.x = (nDetY / maxThreadsPerBlock) + 1;
+
+	map_boudaries_kernel << <blockSize, threadsPerBlock >> > (d_pDetY, nDetYMap, nDetY / 2.0, dv, 0.0);
+
+	blockSize.x = (nPixX / maxThreadsPerBlock) + 1;
+
+	map_boudaries_kernel << <blockSize, threadsPerBlock >> > (d_pObjX, nPixXMap, (double)nPixX, -dx, 0.0);
+
+	blockSize.x = (nPixY / maxThreadsPerBlock) + 1;
+
+	map_boudaries_kernel << <blockSize, threadsPerBlock >> > (d_pObjY, nPixYMap, nPixY / 2.0, dy, 0.0);
+
+	blockSize.x = (nSlices / maxThreadsPerBlock) + 1;
+
+	map_boudaries_kernel << <blockSize, threadsPerBlock >> > (d_pObjZ, nSlices, 0.0, dz, DAG + (dz / 2.0));
+
+	
+	//mexPrintf("Map Boundaries -- Threads:%d Blocks:%d \n", threads, blocks);
+
+	
+	// Initiate variables value with 0
+	cudaMemset(d_pDetZ, 0, nDetYMap * sizeof(double));
+	cudaMemset(d_pVolume, 0, nPixX * nPixY * nSlices * sizeof(double));
+
+	cudaCheckErrors("cudaMemset Zeros");
+	
+	//cudaDeviceSynchronize();
+
+	// X - ray tube initial position
+	double tubeX = 0;
+	double tubeY = 0;
+	double tubeZ = DSD;
+
+	// Iso - center position
+	double isoY = 0;
+	double isoZ = DDR;
+	
+
+	// Integration of 2D projection over the whole projections
+	// (S.1.Integration. - Liu et al(2017))
+
+	// Naive integration o the X coord
+	threadsPerBlock.x = 10;
+	threadsPerBlock.y = 10;
+	threadsPerBlock.z = 10;
+
+	blockSize.x = (unsigned int)ceil(((double)nDetYMap / (threadsPerBlock.x - 1)));
+	blockSize.y = 1;
+	blockSize.z = (unsigned int)ceil((double)nProj / threadsPerBlock.z);
+
+	//mexPrintf("Divisao: %f \n", (double)nDetY / (threadsPerBlock.x - 1));
+	//mexPrintf("Divisao ceil: %d \n", (unsigned int)ceil((double)nDetY / (threadsPerBlock.x - 1)));
+
+	for (int k = 0; k < ceil((double)nDetXMap / (threadsPerBlock.x - 1)); k++) {
+
+		img_integration_kernel << <blockSize, threadsPerBlock >> > (d_pProj, nDetXMap, nDetYMap, integrateXcoord, 0, k * 9, nProj);
+
+		//mexPrintf("integration kernel -- threadsX:%d blocksX:%d threadsY:%d blocksY:%d \n", threadsPerBlock.x, blockSize.x, threadsPerBlock.y, blockSize.y);
+
+	}
+
+	// Naive integration o the Y coord
+	threadsPerBlock.x = 10;
+	threadsPerBlock.y = 10;
+	threadsPerBlock.z = 10;
+
+	blockSize.x = 1;
+	blockSize.y = (unsigned int)ceil((double)nDetXMap / (threadsPerBlock.y - 1));
+	blockSize.z = (unsigned int)ceil((double)nProj / threadsPerBlock.z);
+
+
+	for (int k = 0; k < ceil((double)nDetYMap / (threadsPerBlock.y - 1)); k++) {
+
+		img_integration_kernel << <blockSize, threadsPerBlock >> > (d_pProj, nDetXMap, nDetYMap, integrateYcoord, k * 9, 0, nProj);
+
+		//mexPrintf("integration kernel -- threadsX:%d blocksX:%d threadsY:%d blocksY:%d \n", threadsPerBlock.x, blockSize.x, threadsPerBlock.y, blockSize.y);
+
+	}
+	
+	double* d_pDetmX_tmp = d_pDetmX + (nDetYMap * (nDetXMap-2));
+
+	// Test if we will loop over all projs or not
+	unsigned int projIni, projEnd, nProj2Run;
+	if(idXProj == -1){
+		projIni = 0;
+		projEnd = nProj;
+		nProj2Run = nProj;
+	}
+	else{
+		nProj2Run = 1;
+		projIni = (unsigned int) idXProj;
+		projEnd = (unsigned int) idXProj + 1;
+	}
+	
+	
+	// For each projection
+	for (unsigned int p = projIni; p < projEnd; p++) {
+	
+		// Get specif tube angle for the projection
+		double theta = h_pTubeAngle[p] * M_PI / 180.0;
+
+		// Get specif detector angle for the projection
+		double phi = h_pDetAngle[p] * M_PI / 180.0;
+
+		//mexPrintf("Tube angle:%f Det angle:%f\n", theta, phi);
+
+		// Tube rotation
+		double rtubeY = ((tubeY - isoY)*cos(theta) - (tubeZ - isoZ)*sin(theta)) + isoY;
+		double rtubeZ = ((tubeY - isoY)*sin(theta) + (tubeZ - isoZ)*cos(theta)) + isoZ;
+
+		//mexPrintf("R tube Y:%f R tube Z:%f\n", rtubeY, rtubeZ);
+
+		// Detector rotation
+		threadsPerBlock.x = maxThreadsPerBlock;
+		threadsPerBlock.y = 1;
+		threadsPerBlock.z = 1;
+
+		blockSize.x = (nDetYMap / maxThreadsPerBlock) + 1;
+		blockSize.y = 1;
+		blockSize.z = 1;
+
+		rot_detector_kernel << <blockSize, threadsPerBlock >> > (d_pRdetY, d_pRdetZ, d_pDetY, d_pDetZ, isoY, isoZ, phi, nDetYMap);
+
+		//cudaDeviceSynchronize();
+
+		//mexPrintf("Detector rotation -- Threads:%d Blocks:%d \n", threads, blocks);
+
+
+		// For each slice
+		for (unsigned int nz = 0; nz < nSlices; nz++) {
+			
+			/*
+
+			Map detector onto XY plane(Inside proj loop in case detector rotates)
+
+			*** Note: Matlab has linear indexing as a column of elements, i.e, the elements are actually stored in memory as queued columns.
+			So threads in X are launched in the column direction (DBT Y coord), and threads in Y are launched in the row direction (DBT X coord).
+
+			*/
+		
+			threadsPerBlock.x = 32;
+			threadsPerBlock.y = 32;
+			threadsPerBlock.z = 1;
+
+			blockSize.x = (nDetYMap / threadsPerBlock.x) + 1;
+			blockSize.y = (nDetXMap / threadsPerBlock.y) + 1;
+			blockSize.z = 1;
+
+
+			mapDet2Slice_kernel << <blockSize, threadsPerBlock >> > (d_pDetmX, d_pDetmY, tubeX, rtubeY, rtubeZ, d_pDetX, d_pRdetY, d_pRdetZ, d_pObjZ, nDetXMap, nDetYMap, nz);
+			//cudaDeviceSynchronize();
+
+			//mexPrintf("Map detector onto XY plane -- ThreadsX:%d ThreadsY:%d BlocksX:%d BlocksY:%d\n", threadsPerBlock.x, threadsPerBlock.y, blockSizeX, blockSizeY);
+
+			/*
+			Creates texture memory
+			*/
+
+			/*
+
+			// Allocate CUDA array in device memory
+			cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0,cudaChannelFormatKindFloat);
+
+			cudaArray* d_imageArray;
+			cudaMallocArray(&d_imageArray, &channelDesc, nPixXMap, nPixYMap);
+
+			// Copy to texture memory
+			checkCudaErrors(cudaMemcpyToArray(d_imageArray, 0, 0, d_pSlice, nPixXMap* nPixYMap * sizeof(double), cudaMemcpyDeviceToDevice));
+
+			// Specify texture
+			struct cudaResourceDesc resDesc;
+			memset(&resDesc, 0, sizeof(resDesc));
+			resDesc.resType = cudaResourceTypeArray;
+			resDesc.res.array.array = d_imageArray;
+
+			// Specify texture object parameters
+			struct cudaTextureDesc texDesc;
+			memset(&texDesc, 0, sizeof(texDesc));
+			texDesc.addressMode[0] = cudaAddressModeBorder;
+			texDesc.addressMode[1] = cudaAddressModeBorder;
+			texDesc.filterMode = cudaFilterModeLinear;
+			texDesc.readMode = cudaReadModeElementType;
+			texDesc.normalizedCoords = true;
+
+			// Create texture object
+			cudaTextureObject_t texObj = 0;
+			cudaCreateTextureObject(&texObj, &resDesc, &texDesc, NULL);
+
+			// Allocate result of transformation in device memory
+			double* d_sliceI;
+			cudaMalloc(&d_sliceI, nDetXMap*nDetYMap * sizeof(double));
+
+			// Invoke kernel
+			blockSize.x = (nDetYMap / threadsPerBlock.x) + 1;
+			blockSize.y = (nDetXMap / threadsPerBlock.y) + 1;
+
+			const double2 normFactor = make_double2(nPixX*-dx,
+			nPixY*dy);
+
+			const double2 offset = make_double2(1,
+			0.5);
+
+			bilinear_interp_kernel << <blockSize, threadsPerBlock >> > (d_sliceI, texObj, d_pDetmX, d_pDetmY, nDetXMap, nDetYMap, normFactor, offset);
+
+			mexPrintf("Texture interpolation -- Threads:%d Blocks:%d \n", threads, blocks);
+
+			Destroy texture object
+			cudaDestroyTextureObject(texObj);
+			*/
+
+
+			/*
+			S.2. Interpolation - Liu et al (2017)
+			*/
+
+			blockSize.x = (nPixYMap / threadsPerBlock.x) + 1;
+			blockSize.y = (nPixXMap / threadsPerBlock.y) + 1;
+			
+			bilinear_interpolation_kernel_GPU << <blockSize, threadsPerBlock >> > (d_sliceI, d_pProj, d_pObjX, d_pObjY, d_pDetmX_tmp, d_pDetmY, nPixXMap, nPixYMap, nDetXMap, nDetYMap, nDetX, nDetY, p);
+
+
+			/*
+			S.3. Differentiation - Eq. 24 - Liu et al (2017)
+			*/
+
+			blockSize.x = (nPixY / threadsPerBlock.x) + 1;
+			blockSize.y = (nPixX / threadsPerBlock.y) + 1;
+
+			differentiation_kernel << <blockSize, threadsPerBlock >> > (d_pVolume, d_sliceI, tubeX, rtubeY, rtubeZ, d_pObjX, d_pObjY, d_pObjZ, nPixX, nPixY, nPixXMap, nPixYMap, du, dv, dx, dy, dz, nz);
+
+		} // Loop end slices
+
+	} // Loop end Projections
+
+
+	// Normalize volume dividing by the number of projs
+	threadsPerBlock.x = 10;
+	threadsPerBlock.y = 10;
+	threadsPerBlock.z = 10;
+
+	blockSize.x = (nPixY / threadsPerBlock.x) + 1;
+	blockSize.y = (nPixX / threadsPerBlock.y) + 1;
+	blockSize.z = (nSlices / threadsPerBlock.z) + 1;
+
+	division_kernel << <blockSize, threadsPerBlock >> > (d_pVolume, nPixX, nPixY, nSlices, nProj2Run);
+
+	//double* const h_var1 = (double*)mxMalloc(1 * sizeof(double));
+	//cudaMemcpy((void *)h_var1, (void *)d_pDetmX_tmp, 1 * sizeof(double), cudaMemcpyDeviceToHost);
+	//mexPrintf("P: %d Nz: %d Var:%f \n",p,nz,h_var1[0]);
+
+	//nDetYMap
+	//double* const h_pPixmX = (double*)mxMalloc(nDetXMap*nDetYMap * sizeof(double));
+	//double* const h_pPixmY = (double*)mxMalloc(nDetYMap * sizeof(double));
+	//cudaMemcpy((void *)h_pPixmX, (void *)d_pDetmX, nDetXMap*nDetYMap * sizeof(double), cudaMemcpyDeviceToHost);
+	//cudaMemcpy((void *)h_pPixmY, (void *)d_pDetmY, nDetYMap * sizeof(double), cudaMemcpyDeviceToHost);
+
+	//for (int u = 0; u < nDetXMap; u++)
+	//	for (int v = 0; v < nDetYMap; v++) {
+	//		h_pProj[(0 * nDetYMap*nDetXMap) + (u*nDetYMap) + v] = h_pPixmX[(u*nDetYMap) + v];
+	//		h_pProj[(1 * nDetYMap*nDetXMap) + (u*nDetYMap) + v] = h_pPixmY[v];
+	//	}
+
+	// d_sliceI
+	//double* const h_var = (double*)mxMalloc(nPixXMap*nPixYMap * sizeof(double));
+	//cudaMemcpy((void *)h_var, (void *)d_sliceI, nPixXMap*nPixYMap * sizeof(double), cudaMemcpyDeviceToHost);
+
+	//for (int u = 0; u < nPixXMap; u++)
+	//	for (int v = 0; v < nPixYMap; v++) {
+	//		h_pVolume[(0 * nPixYMap*nPixXMap) + (u*nPixYMap) + v] = h_var[(u*nPixYMap) + v];
+	//	}
+	
+
+	// d_pProj
+	//cudaMemcpy((void *)h_pVolume, (void *)d_pProj, nProj*nDetXMap*nDetYMap * sizeof(double), cudaMemcpyDeviceToHost);
+
+	// d_pVolume
+	cudaMemcpy((void *)h_pVolume, (void *)d_pVolume, nSlices* nPixX * nPixY * sizeof(double), cudaMemcpyDeviceToHost);
+	cudaCheckErrors("cudaMemcpy Final");
+	
+
+	cudaFree(d_pProj);
+	cudaFree(d_sliceI);
+	cudaFree(d_pVolume);
+	cudaFree(d_pTubeAngle);
+	cudaFree(d_pDetAngle);
+	cudaFree(d_pDetX);
+	cudaFree(d_pDetY);
+	cudaFree(d_pDetZ);
+	cudaFree(d_pObjX);
+	cudaFree(d_pObjY);
+	cudaFree(d_pObjZ);
+	cudaFree(d_pDetmY);
+	cudaFree(d_pDetmX);
+	cudaFree(d_pRdetY);
+	cudaFree(d_pRdetZ);
+	
+	cudaCheckErrors("cudaFree Final");
+	
+	sdkStopTimer(&timer);
+	mexPrintf("Processing time: %f (ms)\n", sdkGetTimerValue(&timer));
+	sdkDeleteTimer(&timer);
+
+	cudaDeviceReset();
+
+	return;
+
+}

+ 455 - 0
Functions/Sources/projectionDD_mex/projectionDD_mex.cpp

@@ -0,0 +1,455 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: September, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 projectionDD_mex(data3d,param)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function calculates the volume projection based on the
+%     Distance-Driven principle. It works by calculating the overlap in X
+%     and Y axis of the volume and the detector boundaries.
+%     The geometry is for DBT with half cone-beam. All parameters are set
+%     in "ParameterSettings" code.
+%
+%     INPUT:
+%
+%     - data3d = 3D volume for projection
+%     - param = Parameter of all geometry
+%
+%     OUTPUT:
+%
+%     - proj = projections for each angle.
+%
+%     Reference: Three-Dimensional Digital Tomosynthesis - Yulia
+%     Levakhina (2014), Cap 3.6 and 3.7.
+%
+%     Original Paper: De Man, Bruno, and Samit Basu. "Distance-driven
+%     projection and backprojection in three dimensions." Physics in
+%     Medicine & Biology (2004).
+%
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Distance Driven Projection Code
+*/
+
+#include "projectionDD_mex.h"
+
+/****************************************************************************
+* function: mexFunction() - Matlab interface function.						*
+* INPUTS:																	*
+*   nlhs - Number of expected output mxArrays, specified as an integer.		*
+*   plhs[] - Array of pointers to the expected mxArray output arguments.	*
+*   nrhs - Number of input mxArrays, specified as an integer.				*
+*   prhs[] - Array of pointers to the mxArray input arguments.				*
+****************************************************************************/
+
+void mexFunction(int nlhs, mxArray* plhs[],
+	int nrhs, const mxArray* prhs[]) {
+
+
+	/* Check for proper number of arguments */
+	if (nrhs != 2) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:nargin",
+			"projection_mex requires two input arguments.");
+	}
+	if (nlhs != 1) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:nargout",
+			"projection_mex requires one output argument.");
+	}
+
+	/* Check if the input is of proper type */
+	if (!mxIsSingle(prhs[0])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument has to be single.");
+	}
+	if (!mxIsStruct(prhs[1])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"Second argument has to be a struct.");
+	}
+
+
+	float* pVolume = (float*)mxGetPr(prhs[0]);
+	double* pTubeAngleD = mxGetPr(mxGetField(prhs[1], 0, "tubeDeg"));
+	double* pDetAngleD = mxGetPr(mxGetField(prhs[1], 0, "detectorDeg"));
+
+	const int nProj = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nProj"));
+
+	const int nPixX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nx"));
+	const int nPixY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "ny"));
+	const int nSlices = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nz"));
+	const int nDetX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nu"));
+	const int nDetY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nv"));
+
+	const float dx = (const float)mxGetScalar(mxGetField(prhs[1], 0, "dx"));
+	const float dy = (const float)mxGetScalar(mxGetField(prhs[1], 0, "dy"));
+	const float dz = (const float)mxGetScalar(mxGetField(prhs[1], 0, "dz"));
+	const float du = (const float)mxGetScalar(mxGetField(prhs[1], 0, "du"));
+	const float dv = (const float)mxGetScalar(mxGetField(prhs[1], 0, "dv"));
+
+	const float DSD = (const float)mxGetScalar(mxGetField(prhs[1], 0, "DSD"));
+	const float DDR = (const float)mxGetScalar(mxGetField(prhs[1], 0, "DDR"));
+	const float DAG = (const float)mxGetScalar(mxGetField(prhs[1], 0, "DAG"));
+
+	mwSize NRow = mxGetM(prhs[0]);
+	mwSize NCol = mxGetN(prhs[0]);
+	mwSize NElemVol = mxGetNumberOfElements(prhs[0]);
+
+	/* Check if the input is in proper size */
+	if (NRow != nPixY) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument needs to have the same number of rows as in the configuration file.");
+	}
+
+	if (NCol / nSlices != nPixX) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument needs to have the same number of columns as in the configuration file.");
+	}
+
+	// Cast double vectors to single 
+	float* const pTubeAngle = (float*)mxMalloc(nProj * sizeof(float));
+	float* const pDetAngle = (float*)mxMalloc(nProj * sizeof(float));
+
+	for (int n = 0; n < nProj; n++) {
+		pTubeAngle[n] = (float)pTubeAngleD[n];
+		pDetAngle[n] = (float)pDetAngleD[n];
+	}
+
+	//mexPrintf("Nx:%d Ny:%d Nz:%d \nNu:%d Nv:%d \nDx:%.2f Dy:%.2f Dz:%.2f \nDu:%.2f Dv:%.2f \nDSD:%.2f DDR:%.2f \n", nPixX, nPixY, nSlices, nDetX, nDetY, dx, dy, dz, du, dv, DSD, DDR);
+
+
+	const int nDetXMap = nDetX + 1;
+	const int nDetYMap = nDetY + 1;
+	const int nPixXMap = nPixX + 1;
+	const int nPixYMap = nPixY + 1;
+
+	// Memory allocation for projections coordinates
+	float* const pDetX = (float*)mxMalloc(nDetXMap * sizeof(float));
+	float* const pDetY = (float*)mxMalloc(nDetYMap * sizeof(float));
+	float* const pDetZ = (float*)mxMalloc(nDetYMap * sizeof(float));
+	float* const pObjX = (float*)mxMalloc(nPixXMap * sizeof(float));
+	float* const pObjY = (float*)mxMalloc(nPixYMap * sizeof(float));
+	float* const pObjZ = (float*)mxMalloc(nSlices * sizeof(float));
+
+	// Memory allocation for mapped coordinates
+	float* const pDetmY = (float*)mxMalloc(nDetYMap * sizeof(float));
+	float* const pDetmX = (float*)mxMalloc(nDetYMap * nDetXMap * sizeof(float));
+	float* const pPixmX = (float*)mxMalloc(nPixXMap * sizeof(float));
+	float* const pPixmY = (float*)mxMalloc(nPixYMap * sizeof(float));
+
+
+	// Memory allocation for rotated detecto coords
+	float* const pRdetY = (float*)mxMalloc(nDetYMap * sizeof(float));
+	float* const pRdetZ = (float*)mxMalloc(nDetYMap * sizeof(float));
+
+	// Memory allocation for slice
+	float* const pSlice = (float*)mxMalloc(nPixX * nPixY * sizeof(float));
+
+
+	// Map detector and object boudaries
+	for (int k = nDetX, ind = 0; k >= 0; k--, ind++)					pDetX[ind] = k * du;
+	for (int k = -nDetY / 2, ind = 0; k <= nDetY / 2; k++, ind++)		pDetY[ind] = k * dv;
+	for (int k = 0; k <= nDetY; k++)								pDetZ[k] = 0;
+	for (int k = nPixX, ind = 0; k >= 0; k--, ind++)				pObjX[ind] = k * dx;
+	for (int k = -nPixY / 2, ind = 0; k <= nPixY / 2; k++, ind++)	pObjY[ind] = k * dy;
+	for (int k = 0, ind = 0; k <= nSlices; k++, ind++)				pObjZ[ind] = k * dz + DAG + dz / 2;
+
+
+	// X - ray tube initial position
+	float tubeX = 0;
+	float tubeY = 0;
+	float tubeZ = DSD;
+
+	// Iso - center position
+	float isoY = 0;
+	float isoZ = DDR;
+
+	// Allocate memory for temp projection variable
+	float* const pProjt = (float*)mxMalloc(nDetY * nDetX * nProj * sizeof(float));
+
+	// Initiate temp proj values with zeros
+	for (int p = 0; p < nProj; p++)
+		for (int u = 0; u < nDetX; u++)
+			for (int v = 0; v < nDetY; v++)
+				pProjt[(p * nDetY * nDetX) + (u * nDetY) + v] = 0;
+
+	// For each projection
+	for (int p = 0; p < nProj; p++) {
+
+		// Get specif tube angle for the projection
+		float theta = pTubeAngle[p] * M_PI / 180.f;
+
+		// Get specif detector angle for the projection
+		float phi = pDetAngle[p] * M_PI / 180.f;
+
+
+		// Tubre rotation
+		float rtubeY = ((tubeY - isoY) * (float)cos(theta) - (tubeZ - isoZ) * (float)sin(theta)) + isoY;
+		float rtubeZ = ((tubeY - isoY) * (float)sin(theta) + (tubeZ - isoZ) * (float)cos(theta)) + isoZ;
+
+		// Detector rotation
+		for (int v = 0; v < nDetYMap; v++) {
+			pRdetY[v] = ((pDetY[v] - isoY) * (float)cos(phi) - (pDetZ[v] - isoZ) * (float)sin(phi)) + isoY;
+			pRdetZ[v] = ((pDetY[v] - isoY) * (float)sin(phi) + (pDetZ[v] - isoZ) * (float)cos(phi)) + isoZ;
+		}
+
+
+		// Map detector onto XY plane(Inside proj loop in case detector rotates)
+		mapp2xy(pDetmX, pDetmY, tubeX, rtubeY, rtubeZ, pDetX, pRdetY, pRdetZ, nDetXMap, nDetYMap);
+
+
+		// Pixel start index and increment
+		int detIstart = 0;
+		int detIinc = 1;
+
+		// Mapped detector length
+		float deltaDetmY = pDetmY[detIstart + detIinc] - pDetmY[detIstart];
+
+
+		// For each slice
+		for (int z = 0; z < nSlices; z++) {
+
+			// Flip X(Img coord is reverse to Global)
+			for (int x = 0, x_inv = nPixX - 1; x < nPixX; x++, x_inv--)
+				for (int y = 0; y < nPixY; y++)
+					pSlice[(x * nPixY) + y] = pVolume[(z * nPixX * nPixY) + (x_inv * nPixY) + y];
+
+			// Tmp Z coords value for Y direction
+			float* const pObjZt = (float*)mxMalloc(nPixYMap * sizeof(float));
+
+			// Tmp Pixel X mapped coords
+			float* const pPixmXt = (float*)mxMalloc(nPixYMap * nPixXMap * sizeof(float));
+
+			// Get specif Z coord value for each slice
+			for (int k = 0; k < nPixYMap; k++)	pObjZt[k] = pObjZ[z];
+
+			// Map slice onto XY plane
+			mapp2xy(pPixmXt, pPixmY, tubeX, rtubeY, rtubeZ, pObjX, pObjY, pObjZt, nPixXMap, nPixYMap);
+
+			// Flip X(Img coord is reverse to Global)
+			for (int x = 0, x_inv = nPixXMap - 1; x < nPixXMap; x++, x_inv--)
+				pPixmX[x] = pPixmXt[x_inv * nPixYMap];
+
+			// Free temp variables
+			mxFree(pObjZt);
+			mxFree(pPixmXt);
+
+			// Pixel start index and increment
+			int pixIstart = 0;
+			int pixIinc = 1;
+
+			// Mapped pixel length
+			float deltaPixmX = pPixmX[pixIstart + pixIinc] - pPixmX[pixIstart];
+			float deltaPixmY = pPixmY[pixIstart + pixIinc] - pPixmY[pixIstart];
+
+			// Start pixel and detector indices
+			int detIndY = detIstart;
+			int pixIndY = pixIstart;
+
+			// Case 1
+			// Find first detector overlap maped with pixel maped on Y
+			if (pDetmY[detIndY] - pPixmY[pixIstart] < -deltaDetmY)
+				while (pDetmY[detIndY] - pPixmY[pixIstart] < -deltaDetmY)
+					detIndY = detIndY + detIinc;
+
+			else
+				// Case 2
+				// Find first pixel overlap maped with detector maped on Y
+				if (pDetmY[detIstart] - pPixmY[pixIndY] > deltaPixmY)
+					while (pDetmY[detIstart] - pPixmY[pixIndY] > deltaPixmY)
+						pixIndY = pixIndY + pixIinc;
+
+			float moving_left_boundaryY = NULL;
+
+			// Get the left coordinate of the first overlap on Y axis
+			if (pDetmY[detIndY] < pPixmY[pixIndY])
+				moving_left_boundaryY = pPixmY[pixIndY];
+			else
+				moving_left_boundaryY = pDetmY[detIndY];
+
+
+			// Allocate memory for specif row of X map detector coords
+			float* const pDetmXrow = (float*)mxMalloc(nDetXMap * sizeof(float));
+
+			float overLapY = NULL;
+
+			// Loop over Y intersections
+			while ((detIndY < nDetY) && (pixIndY < nPixY)) {
+
+				float alpha = (float)atan((pDetmY[detIndY] + (deltaDetmY / 2) - rtubeY) / rtubeZ);
+
+				// Case A, when you jump to the next detector boundarie but stay
+				// in the same pixel
+				if (pDetmY[detIndY + 1] <= pPixmY[pixIndY + 1])
+					overLapY = (pDetmY[detIndY + 1] - moving_left_boundaryY) / deltaDetmY; // Normalized overlap Calculation
+
+				else
+					// Case B, when you jump to the next pixel boundarie but stay
+					// in the same detector
+					overLapY = (pPixmY[pixIndY + 1] - moving_left_boundaryY) / deltaDetmY; // Normalized overlap Calculation
+
+				//										***** X overlap *****
+				int detIndX = detIstart;
+				int pixIndX = pixIstart;
+
+
+				// Get row / coll of X flipped, which correspond to that Y overlap det
+				for (int x = 0, x_inv = nDetXMap - 1; x < nDetXMap; x++, x_inv--)
+					pDetmXrow[x] = pDetmX[(x_inv * nDetYMap) + detIndY];
+
+				// Mapped detecor length on X
+				float deltaDetmX = pDetmXrow[detIstart + detIinc] - pDetmXrow[detIstart];
+
+				// Case 1
+				// Find first detector overlap maped with pixel maped on X
+				if (pDetmXrow[detIndX] - pPixmX[pixIstart] < -deltaDetmX)
+					while (pDetmXrow[detIndX] - pPixmX[pixIstart] < -deltaDetmX)
+						detIndX = detIndX + detIinc;
+
+				else
+					// Case 2
+					// Find first pixel overlap maped with detector maped on X
+					if (pDetmXrow[detIstart] - pPixmX[pixIndX] > deltaPixmX)
+						while (pDetmXrow[detIstart] - pPixmX[pixIndY] > deltaPixmX)
+							pixIndX = pixIndX + pixIinc;
+
+				float moving_left_boundaryX = NULL;
+
+				// Get the left coordinate of the first overlap on X axis
+				if (pDetmXrow[detIndX] < pPixmX[pixIndX])
+					moving_left_boundaryX = pPixmX[pixIndX];
+				else
+					moving_left_boundaryX = pDetmXrow[detIndX];
+
+
+				// Loop over X intersections
+				while ((detIndX < nDetX) && (pixIndX < nPixX)) {
+
+					float gamma = (float)atan((pDetmXrow[detIndX] + (deltaDetmX / 2) - tubeX) / rtubeZ);
+
+					// Case A, when you jump to the next detector boundarie but stay
+					// in the same pixel
+					if (pDetmXrow[detIndX + 1] <= pPixmX[pixIndX + 1]) {
+
+						float overLapX = (pDetmXrow[detIndX + 1] - moving_left_boundaryX) / deltaDetmX; // Normalized overlap Calculation
+
+						pProjt[(p * nDetY * nDetX) + (detIndX * nDetY) + detIndY] += overLapX * overLapY * pSlice[pixIndX * nPixY + pixIndY] * dz / ((float)cos(alpha) * (float)cos(gamma));
+
+						detIndX = detIndX + detIinc;
+						moving_left_boundaryX = pDetmXrow[detIndX];
+					}
+					else {
+						// Case B, when you jump to the next pixel boundarie but stay
+						// in the same detector
+
+						float overLapX = (pPixmX[pixIndX + 1] - moving_left_boundaryX) / deltaDetmX; // Normalized overlap Calculation
+
+						pProjt[(p * nDetY * nDetX) + (detIndX * nDetY) + detIndY] += overLapX * overLapY * pSlice[pixIndX * nPixY + pixIndY] * dz / ((float)cos(alpha) * (float)cos(gamma));
+
+						pixIndX = pixIndX + pixIinc;
+						moving_left_boundaryX = pPixmX[pixIndX];
+
+					}
+
+				}
+				//										***** Back to Y overlap *****
+
+				// Case A, when you jump to the next detector boundarie but stay
+				// in the same pixel
+				if (pDetmY[detIndY + 1] <= pPixmY[pixIndY + 1]) {
+					detIndY = detIndY + detIinc;
+					moving_left_boundaryY = pDetmY[detIndY];
+				}
+				else {
+					// Case B, when you jump to the next pixel boundarie but stay
+					// in the same detector
+					pixIndY = pixIndY + pixIinc;
+					moving_left_boundaryY = pPixmY[pixIndY];
+				}
+
+			} // Y Overlap loop
+
+			// Free memory
+			mxFree(pDetmXrow);
+
+		} // Loop end slices
+
+	} // Loop end Projections
+
+
+	// Free memory
+	mxFree(pSlice);
+	mxFree(pDetX);
+	mxFree(pDetY);
+	mxFree(pDetZ);
+	mxFree(pObjX);
+	mxFree(pObjY);
+	mxFree(pObjZ);
+	mxFree(pDetmY);
+	mxFree(pDetmX);
+	mxFree(pPixmX);
+	mxFree(pPixmY);
+	mxFree(pRdetY);
+	mxFree(pRdetZ);
+
+	// Output memory allocation
+	mwSize dims[3] = { nDetY,nDetX,nProj };
+	plhs[0] = mxCreateNumericArray(3, dims, mxSINGLE_CLASS, mxREAL);
+	float* pProj = (float*)mxGetData(plhs[0]);
+
+	// Flip projection back X(Img coord is reverse to Global)
+	for (int p = 0; p < nProj; p++)
+		for (int x = 0, x_inv = nDetX - 1; x < nDetX; x++, x_inv--)
+			for (int y = 0; y < nDetY; y++)
+				pProj[(p * nDetX * nDetY) + (x * nDetY) + y] = pProjt[(p * nDetX * nDetY) + (x_inv * nDetY) + y];
+
+	// Free memory
+	mxFree(pProjt);
+
+
+	return;
+}
+
+// Map on XY plane
+void mapp2xy(float* const pXmapp,
+	float* const pYmapp,
+	float tubeX,
+	float tubeY,
+	float tubeZ,
+	float* const pXcoord,
+	float* const pYcoord,
+	float* const pZcoord,
+	const int nXelem,
+	const int nYelem) {
+
+
+	for (int x = 0; x < nXelem; x++)
+		for (int y = 0; y < nYelem; y++) {
+
+			int ind = (x * nYelem) + y;
+			pXmapp[ind] = pXcoord[x] + pZcoord[y] * (pXcoord[x] - tubeX) / (tubeZ - pZcoord[y]);
+
+			if (x == 0)
+				pYmapp[y] = pYcoord[y] + pZcoord[y] * (pYcoord[y] - tubeY) / (tubeZ - pZcoord[y]);
+		}
+
+
+	return;
+}

+ 41 - 0
Functions/Sources/projectionDD_mex/projectionDD_mex.h

@@ -0,0 +1,41 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: September, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This is the header function
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Distance Driven Projection Code
+*/
+
+//typedef double user_dataType;
+//typedef float user_dataType;
+//#define user_mexdataType mxDOUBLE_CLASS
+//#define user_mexdataType mxSINGLE_CLASS
+
+#include "mex.h"
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+void mapp2xy(float* const pXmapp, float* const pYmapp, float tubeX, float tubeY, float tubeZ, float* const pXcoord, float* const pYcoord, float* const pZcoord, const int nXelem, const int nYelem);

+ 31 - 0
Functions/Sources/projectionDD_mex/projectionDD_mex.sln

@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29025.244
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "projectionDD_mex", "projectionDD_mex.vcxproj", "{1304D4F4-0F8A-47E4-9CC9-E8490D8164D3}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{1304D4F4-0F8A-47E4-9CC9-E8490D8164D3}.Debug|x64.ActiveCfg = Debug|x64
+		{1304D4F4-0F8A-47E4-9CC9-E8490D8164D3}.Debug|x64.Build.0 = Debug|x64
+		{1304D4F4-0F8A-47E4-9CC9-E8490D8164D3}.Debug|x86.ActiveCfg = Debug|Win32
+		{1304D4F4-0F8A-47E4-9CC9-E8490D8164D3}.Debug|x86.Build.0 = Debug|Win32
+		{1304D4F4-0F8A-47E4-9CC9-E8490D8164D3}.Release|x64.ActiveCfg = Release|x64
+		{1304D4F4-0F8A-47E4-9CC9-E8490D8164D3}.Release|x64.Build.0 = Release|x64
+		{1304D4F4-0F8A-47E4-9CC9-E8490D8164D3}.Release|x86.ActiveCfg = Release|Win32
+		{1304D4F4-0F8A-47E4-9CC9-E8490D8164D3}.Release|x86.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {79303CF4-4DDB-4F8F-ABB0-5E03781B09DE}
+	EndGlobalSection
+EndGlobal

+ 147 - 0
Functions/Sources/projectionDD_mex/projectionDD_mex.vcxproj

@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <ProjectGuid>{1304D4F4-0F8A-47E4-9CC9-E8490D8164D3}</ProjectGuid>
+    <RootNamespace>projectionDDmex</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <TargetExt>.mexw64</TargetExt>
+    <IncludePath>C:\Program Files\MATLAB\R2017b\extern\include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <TargetExt>.mexw64</TargetExt>
+    <IncludePath>C:\Program Files\MATLAB\R2017b\extern\include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libut.lib;libmx.lib;libmex.lib;libmat.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalOptions>/export:mexFunction %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libut.lib;libmx.lib;libmex.lib;libmat.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalOptions>/export:mexFunction %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="projectionDD_mex.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="projectionDD_mex.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 618 - 0
Functions/Sources/projectionDDb_mex/projectionDDb_mex.cpp

@@ -0,0 +1,618 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: March, 2019
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 projectionDDb_mex(data3d,param)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function calculates the volume projection based on the
+%     Branchless Distance-Driven principle.
+%     The geometry is for DBT with half cone-beam. All parameters are set
+%     in "ParameterSettings" code.
+%
+%     INPUT:
+%
+%     - data3d = 3D volume for projection
+%     - param = Parameter of all geometry
+%
+%     OUTPUT:
+%
+%     - proj = projections for each angle.
+%
+%     Reference:
+%     - Branchless Distance Driven Projection and Backprojection,
+%     Samit Basu and Bruno De Man (2006)
+%     - GPU Acceleration of Branchless Distance Driven Projection and
+%     Backprojection, Liu et al (2016)
+%     - GPU-Based Branchless Distance-Driven Projection and Backprojection,
+%     Liu et al (2017)
+%     - A GPU Implementation of Distance-Driven Computed Tomography,
+%     Ryan D. Wagner (2017)
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Projection Branchless Distance Driven Code (CPU-Multithread)
+*/
+
+#include "projectionDDb_mex.h"
+
+
+/****************************************************************************
+* function: mexFunction() - Matlab interface function.						*
+* INPUTS:																	*
+*   nlhs - Number of expected output mxArrays, specified as an integer.		*
+*   plhs[] - Array of pointers to the expected mxArray output arguments.	*
+*   nrhs - Number of input mxArrays, specified as an integer.				*
+*   prhs[] - Array of pointers to the mxArray input arguments.				*
+****************************************************************************/
+
+void mexFunction(int nlhs, mxArray *plhs[],
+	int nrhs, const mxArray *prhs[]) {
+
+
+	/* Check for proper number of arguments */
+	if (nrhs != 2) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:nargin",
+			"projection_mex requires two input arguments.");
+	}
+	if (nlhs != 1) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:nargout",
+			"projection_mex requires one output argument.");
+	}
+
+	/* Check if the input is of proper type */
+	if (!mxIsDouble(prhs[0])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument has to be double.");
+	}
+	if (!mxIsStruct(prhs[1])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"Second argument has to be a struct.");
+	}
+
+	double* const pVolume = (double*)(mxGetPr(prhs[0])); // This variable has to come as single/double
+	double* const pTubeAngleD = mxGetPr(mxGetField(prhs[1], 0, "tubeDeg"));
+	double* const pDetAngleD = mxGetPr(mxGetField(prhs[1], 0, "detectorDeg"));
+
+
+	const int unsigned nProj = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nProj"));
+
+	const int unsigned nPixX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nx"));
+	const int unsigned nPixY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "ny"));
+	const int unsigned nSlices = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nz"));
+	const int unsigned nDetX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nu"));
+	const int unsigned nDetY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nv"));
+
+	const double dx = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dx"));
+	const double dy = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dy"));
+	const double dz = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dz"));
+	const double du = (const double)mxGetScalar(mxGetField(prhs[1], 0, "du"));
+	const double dv = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dv"));
+
+	const double DSD = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DSD"));
+	const double DDR = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DDR"));
+	const double DAG = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DAG"));
+
+	mwSize NRow = mxGetM(prhs[0]);
+	mwSize NCol = mxGetN(prhs[0]);
+	mwSize NElemVol = mxGetNumberOfElements(prhs[0]);
+
+	/* Check if the input is in proper size */
+	if (NRow != nPixY) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument needs to have the same number of rows as in the configuration file.");
+	}
+
+	if (NCol / nSlices != nPixX) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument needs to have the same number of columns as in the configuration file.");
+	}
+
+	// Cast single vectors to double 
+	double* const pTubeAngle = (double*)mxMalloc(nProj * sizeof(double));
+	double* const pDetAngle = (double*)mxMalloc(nProj * sizeof(double));
+
+	for (unsigned int n = 0; n < nProj; n++) {
+		pTubeAngle[n] = (double)pTubeAngleD[n];
+		pDetAngle[n] = (double)pDetAngleD[n];
+	}
+
+	//mexPrintf("Nx:%d Ny:%d Nz:%d \nNu:%d Nv:%d \nDx:%.2f Dy:%.2f Dz:%.2f \nDu:%.2f Dv:%.2f \nDSD:%.2f DDR:%.2f \n", nPixX, nPixY, nSlices, nDetX, nDetY, dx, dy, dz, du, dv, DSD, DDR);
+
+
+	// Output memory allocation
+	mwSize dims[3] = { nDetY,nDetX,nProj };
+
+	plhs[0] = mxCreateNumericArray(3, dims, mxDOUBLE_CLASS, mxREAL);
+	double* const pProj = (double*)mxGetData(plhs[0]);
+
+	projectionDDb(pProj, pVolume, pTubeAngle, pDetAngle, nProj, nPixX, nPixY, nSlices, nDetX, nDetY, dx, dy, dz, du, dv, DSD, DDR, DAG);
+
+	mxFree(pTubeAngle);
+	mxFree(pDetAngle);
+
+	return;
+
+}
+
+
+// CPU Branchless Distance-driven projection
+void projectionDDb(double* const pProj,
+	double* const pVolume,
+	double* const pTubeAngle,
+	double* const pDetAngle,
+	const  int nProj,
+	const  int nPixX,
+	const  int nPixY,
+	const  int nSlices,
+	const  int nDetX,
+	const  int nDetY,
+	const double dx,
+	const double dy,
+	const double dz,
+	const double du,
+	const double dv,
+	const double DSD,
+	const double DDR,
+	const double DAG)
+{
+
+	nThreads = omp_get_max_threads();
+
+	mexPrintf("CPU running with maximum number of threads: %d \n", nThreads);
+	
+	clock_t time;
+
+	time = clock();
+	
+	// Number of mapped detector and pixels
+	const int nDetXMap = nDetX + 1;
+	const int nDetYMap = nDetY + 1;
+	const int nPixXMap = nPixX + 1;
+	const int nPixYMap = nPixY + 1;
+
+
+	// Allocate memory
+	double* const projI = (double*)mxMalloc(nDetXMap*nDetYMap * sizeof(double));
+
+
+	// Pointer for projections coordinates
+	// Allocate memory for projections coordinates
+	double* const pDetX = (double*)mxMalloc(nDetXMap * sizeof(double));
+	double* const pDetY = (double*)mxMalloc(nDetYMap * sizeof(double));
+	double* const pDetZ = (double*)mxMalloc(nDetYMap * sizeof(double));
+	double* const pObjX = (double*)mxMalloc(nPixXMap * sizeof(double));
+	double* const pObjY = (double*)mxMalloc(nPixYMap * sizeof(double));
+	double* const pObjZ = (double*)mxMalloc(nSlices * sizeof(double));
+
+
+	// Pointer for mapped coordinates
+	// Allocate memory for mapped coordinates
+	double* const pDetmY = (double*)mxMalloc(nDetYMap * sizeof(double));
+	double* const pDetmX = (double*)mxMalloc(nDetYMap * nDetXMap * sizeof(double));
+
+
+
+	// Pointer for rotated detector coords
+	// Allocate memory for rotated detector coords
+	double* const pRdetY = (double*)mxMalloc(nDetYMap * sizeof(double));
+	double* const pRdetZ = (double*)mxMalloc(nDetYMap * sizeof(double));
+
+
+	// Map detector and object boudaries
+	mapBoudaries(pDetX, nDetXMap, (double)nDetX, -du, 0.0);
+
+	mapBoudaries(pDetY, nDetYMap, nDetY / 2.0, dv, 0.0);
+
+	mapBoudaries(pDetZ, nDetYMap, 0.0, 0.0, 0.0);
+
+	mapBoudaries(pObjX, nPixXMap, (double)nPixX, -dx, 0.0);
+
+	mapBoudaries(pObjY, nPixYMap, nPixY / 2.0, dy, 0.0);
+
+	mapBoudaries(pObjZ, nSlices, 0.0, dz, DAG + (dz / 2.0));
+
+
+	// X - ray tube initial position
+	double tubeX = 0;
+	double tubeY = 0;
+	double tubeZ = DSD;
+
+	// Iso - center position
+	double isoY = 0;
+	double isoZ = DDR;
+
+
+	// Allocate memory for temp projection variable
+	double* const pProjt = (double*)mxMalloc(nDetY *nDetX * nProj * sizeof(double));
+	double* const pVolumet = (double*)mxMalloc(nPixYMap *nPixXMap * nSlices * sizeof(double));
+
+	
+	// Initiate variables value with 0
+	for (int p = 0; p < nProj; p++)
+		for (int x = 0; x < nDetX; x++)
+			for (int y = 0; y < nDetY; y++)
+				pProjt[(p*nDetY *nDetX) + (x*nDetY) + y] = 0;
+	
+
+	// Integration of 2D slices over the whole volume
+	// (S.1.Integration. - Liu et al(2017))
+	/*
+		
+		2 - D image
+
+		 -->J
+		|	-------------
+		v	|			|
+		I	|			|
+			|			|
+			|			|
+			-------------
+	*/
+	
+	
+	// Initialize first column and row with zeros
+	for (int nz = 0; nz < nSlices; nz++) {
+
+		for (int y = 0; y < nPixYMap; y++)
+			pVolumet[(nz*nPixYMap *nPixXMap) + y] = 0;
+		
+		for (int x = 1; x < nPixXMap; x++)
+			pVolumet[(nz*nPixYMap *nPixXMap) + (x*nPixYMap)] = 0;
+	}
+		
+
+	// Integrate on I direction
+	for (int nz = 0; nz < nSlices; nz++) 
+		#pragma omp parallel for num_threads(nThreads) schedule(static)
+		for (int x = 0; x < nPixX; x++){
+			double sum = 0;
+			for (int y = 0; y < nPixY; y++){
+				sum += pVolume[(nz*nPixY *nPixX) + (x*nPixY) + y];
+				pVolumet[(nz*nPixYMap *nPixXMap) + ((x+1)*nPixYMap) + y + 1] = sum;
+			}
+		}
+
+	// Integrate on J direction
+	for (int nz = 0; nz < nSlices; nz++) 	
+		#pragma omp parallel for num_threads(nThreads) schedule(static)
+		for (int y = 1; y < nPixYMap; y++) 
+			for (int x = 2; x < nPixXMap; x++) 
+				pVolumet[(nz*nPixYMap *nPixXMap) + (x*nPixYMap) + y] += pVolumet[(nz*nPixYMap *nPixXMap) + ((x - 1)*nPixYMap) + y];	
+
+
+
+	// For each projection
+	for (int p = 0; p < nProj; p++) {
+		
+		// Get specif tube angle for the projection
+		double theta = pTubeAngle[p] * M_PI / 180.0;
+
+		// Get specif detector angle for the projection
+		double phi = pDetAngle[p] * M_PI / 180.0;
+
+		//mexPrintf("Tube angle:%f Det angle:%f\n", theta, phi);
+
+		// Tube rotation
+		double rtubeY = ((tubeY - isoY)*cos(theta) - (tubeZ - isoZ)*sin(theta)) + isoY;
+		double rtubeZ = ((tubeY - isoY)*sin(theta) + (tubeZ - isoZ)*cos(theta)) + isoZ;
+
+		//mexPrintf("R tube Y:%f R tube Z:%f\n", rtubeY, rtubeZ);
+
+		// Detector rotation
+		for (int y = 0; y < nDetYMap; y++) {
+			pRdetY[y] = ((pDetY[y] - isoY)*cos(phi) - (pDetZ[y] - isoZ)*sin(phi)) + isoY;
+			pRdetZ[y] = ((pDetY[y] - isoY)*sin(phi) + (pDetZ[y] - isoZ)*cos(phi)) + isoZ;
+		}
+
+
+		// For each slice
+		for (int nz = 0; nz < nSlices; nz++) {
+			
+			/*
+
+			Map detector onto XY plane(Inside proj loop in case detector rotates)
+
+			*** Note: Matlab has linear indexing as a column of elements, i.e, the elements are actually stored in memory as queued columns.
+	
+			*/
+
+			// Map slice onto XY plane
+			mapDet2Slice(pDetmX, pDetmY, tubeX, rtubeY, rtubeZ, pDetX, pRdetY, pRdetZ, pObjZ[nz], nDetXMap, nDetYMap);
+
+			/*
+			S.2. Interpolation - Liu et al (2017)
+			*/
+
+			bilinear_interpolation(projI, pVolumet, pDetmX, pDetmY, nDetXMap, nDetYMap, nPixXMap, nPixYMap, dx, nz);
+
+			/*
+			S.3. Differentiation - Eq. 24 - Liu et al (2017)
+			*/
+
+			differentiation(pProjt, projI, pDetmX, pDetmY, tubeX, rtubeY, rtubeZ, pDetX, pRdetY, pRdetZ, nDetX, nDetY, nDetXMap, nDetYMap, du, dv, dx, dy, dz, p);
+
+		} // Loop end slices
+
+	} // Loop end Projections
+
+	
+	// Copy pProjt to pProj
+	for (int p = 0; p < nProj; p++)
+		for (int x = 0; x < nDetX; x++)
+			for (int y = 0; y < nDetY; y++)
+				pProj[(p*nDetX*nDetY) + (x*nDetY) + y] = pProjt[(p*nDetX*nDetY) + (x*nDetY) + y];
+	
+	
+	// Interp
+	//for (int x = 0; x < nDetXMap; x++)
+	//	for (int y = 0; y < nDetYMap; y++) {
+	//		pProj[(0 * nDetXMap*nDetYMap) + (x*nDetYMap) + y] = projI[(x*nDetYMap) + y];
+	//	}
+
+	// Volume			
+	//for (int p = 0; p < nSlices; p++)
+	//	for (int x = 0; x < nPixXMap; x++)
+	//		for (int y = 0; y < nPixYMap+1; y++) {
+	//			pProj[(p * nPixXMap*nPixYMap) + (x*nPixYMap) + y] = pVolumet[(p * nPixXMap*nPixYMap) + (x*nPixYMap) + y];
+	//		}
+	
+	mxFree(pProjt);
+	mxFree(projI);
+	mxFree(pVolumet);
+	mxFree(pDetX);
+	mxFree(pDetY);
+	mxFree(pDetZ);
+	mxFree(pObjX);
+	mxFree(pObjY);
+	mxFree(pObjZ);
+	mxFree(pDetmY);
+	mxFree(pDetmX);
+	mxFree(pRdetY);
+	mxFree(pRdetZ);
+	
+	time = clock() - time;
+	mexPrintf("Processing time: %f seconds.\n", ((double)time) / CLOCKS_PER_SEC);
+
+	return;	
+}
+
+
+// Make boundaries of detector and slices
+void mapBoudaries(double* pBound, 
+	const int nElem, 
+	const double valueLeftBound, 
+	const double sizeElem, 
+	const double offset){
+
+	for (int k = 0; k < nElem; k++)
+		pBound[k] = (k - valueLeftBound) * sizeElem + offset;
+
+	return;
+}
+
+// Map on XY plane
+void mapDet2Slice(double* const pXmapp,
+	double* const pYmapp,
+	double tubeX,
+	double tubeY,
+	double tubeZ,
+	double * const pXcoord,
+	double * const pYcoord,
+	double * const pZcoord,
+	double ZSlicecoord,
+	const int nXelem,
+	const int nYelem){
+
+	#pragma omp parallel num_threads(nThreads) 
+	{
+		int ind;
+		#pragma omp for schedule(static)
+			for (int x = 0; x < nXelem; x++)
+				for (int y = 0; y < nYelem; y++) {
+
+					ind = (x*nYelem) + y;
+					pXmapp[ind] = ((pXcoord[x] - tubeX)*(ZSlicecoord - pZcoord[y]) - (pXcoord[x] * tubeZ) + (pXcoord[x] * pZcoord[y])) / (-tubeZ + pZcoord[y]);
+
+					if (x == 0)
+						pYmapp[y] = ((pYcoord[y] - tubeY)*(ZSlicecoord - pZcoord[y]) - (pYcoord[y] * tubeZ) + (pYcoord[y] * pZcoord[y])) / (-tubeZ + pZcoord[y]);
+				}
+	}
+
+	return;
+}
+
+// Bilinear interpolation
+void bilinear_interpolation(double* projI, 
+	double* pVolume, 
+	double* pDetmX, 
+	double* pDetmY, 
+	const int nDetXMap, 
+	const int nDetYMap,
+	const int nPixXMap, 
+	const int nPixYMap, 
+	const double pixelSize,
+	const unsigned int nz) {
+
+
+	/*
+
+	S.2. Interpolation - Liu et al (2017)
+
+	Reference:
+	- https://en.wikipedia.org/wiki/Bilinear_interpolation
+	- https://stackoverflow.com/questions/21128731/bilinear-interpolation-in-c-c-and-cuda
+
+	Note: *** We are using the Unit Square equation ***
+
+	alpha = X - X1
+	1-alpha = X2 - X
+
+	beta = Y - Y1
+	1-beta = Y2 - Y
+
+	----> Xcoord 
+	|				0___________
+	v				|_d00_|_d10_|
+	Ycoord			|_d01_|_d11_|
+
+	*/
+
+	// These are the boundaries of the slices, ranging from (0-nPixX)
+	const double PixXbound = (double) (nPixXMap - 1);
+	const double PixYbound = (double) (nPixYMap - 1);
+
+	#pragma omp parallel for num_threads(nThreads) schedule(static) 
+	for (int x = 0; x < nDetXMap; x++)
+		for (int y = 0; y < nDetYMap; y++) {
+
+			// Adjust the mapped coordinates to cross the range of (0-nPixX).*dx 
+			// Divide by pixelSize to get a unitary pixel size
+			double xNormData = PixXbound - pDetmX[x * nDetYMap + y] / pixelSize;
+			signed int xData = (signed int)floor(xNormData);
+			double alpha = xNormData - xData;
+
+			// Adjust the mapped coordinates to cross the range of (0-nPixY).*dy  
+			// Divide by pixelSize to get a unitary pixel size
+			double yNormData = (PixYbound / 2.0) + (pDetmY[y] / pixelSize);
+			signed int yData = (signed int)floor(yNormData);
+			double beta = yNormData - yData;
+
+			double d00, d01, d10, d11;
+			if (((xNormData) >= 0) && ((xNormData) <= PixXbound) && ((yNormData) >= 0) && ((yNormData) <= PixYbound))	d00 = pVolume[(nz*nPixYMap*nPixXMap) + (xData*nPixYMap + yData)];					else    d00 = 0.0;
+			if (((xData + 1) > 0) && ((xData + 1) <= PixXbound) && ((yNormData) >= 0) && ((yNormData) <= PixYbound))	d10 = pVolume[(nz*nPixYMap*nPixXMap) + ((xData + 1)*nPixYMap + yData)]; 			else    d10 = 0.0;
+			if (((xNormData) >= 0) && ((xNormData) <= PixXbound) && ((yData + 1) > 0) && ((yData + 1) <= PixYbound))	d01 = pVolume[(nz*nPixYMap*nPixXMap) + (xData*nPixYMap + yData + 1)]; 				else    d01 = 0.0;
+			if (((xData + 1) > 0) && ((xData + 1) <= PixXbound) && ((yData + 1) > 0) && ((yData + 1) <= PixYbound))		d11 = pVolume[(nz*nPixYMap*nPixXMap) + ((xData + 1)*nPixYMap + yData + 1)];			else    d11 = 0.0;
+
+			double result_temp1 = alpha * d10 + (-d00 * alpha + d00);
+			double result_temp2 = alpha * d11 + (-d01 * alpha + d01);
+
+			projI[x * nDetYMap + y] = beta * result_temp2 + (-result_temp1 * beta + result_temp1);
+
+		}
+		
+	return;
+}
+
+// Differentiation
+void differentiation(double* pProj, 
+	double* projI, 
+	double* const pDetmX, 
+	double* const pDetmY, 
+	double tubeX, 
+	double rtubeY, 
+	double rtubeZ,
+	double* const pDetX, 
+	double* const pRdetY, 
+	double* const pRdetZ, 
+	const int nDetX, 
+	const int nDetY, 
+	const int nDetXMap,
+	const int nDetYMap, 
+	const double du, 
+	const double dv, 
+	const double dx, 
+	const double dy, 
+	const double dz, 
+	const unsigned int p) {
+
+
+	/*
+
+	S.3. Differentiation - Eq. 24 - Liu et al (2017)
+
+	Detector integral projection
+	___________
+	|_A_|_B_|___|
+	|_C_|_D_|___|
+	|___|___|___|
+
+
+	(y,x)
+	________________
+	|_A_|__B__|_____|
+	|_C_|(0,0)|(0,1)|
+	|___|(1,0)|(1,1)|
+
+
+	Coordinates on intergal projection:
+
+	A = x * nDetYMap + y
+	B = ((x+1) * nDetYMap) + y
+	C = x * nDetYMap + y + 1
+	D = ((x+1) * nDetYMap) + y + 1
+
+	*/
+
+	#pragma omp parallel num_threads(nThreads)
+	{
+		unsigned int coordA;
+		unsigned int coordB;
+		unsigned int coordC;
+		unsigned int coordD;
+
+		double detMapSizeX;
+		double detMapSizeY;
+		double gamma;
+		double alpha;
+
+		double dA, dB, dC, dD;
+
+		#pragma omp for schedule(static)
+		for (int x = 0; x < nDetX; x++)
+			for (int y = 0; y < nDetY; y++) {
+
+				coordA = x * nDetYMap + y;
+				coordB = ((x + 1) * nDetYMap) + y;
+				coordC = coordA + 1;
+				coordD = coordB + 1;
+
+
+				// Detector X coord overlap calculation
+				detMapSizeX = pDetmX[coordA] - pDetmX[coordB];
+
+				// Detector Y coord overlap calculation
+				detMapSizeY = pDetmY[y + 1] - pDetmY[y];
+
+				// x - ray angle in X coord
+				gamma = atan((pDetX[x] + (du / 2) - tubeX) / rtubeZ);
+
+				// x - ray angle in Y coord
+				alpha = atan((pRdetY[y] + (dv / 2) - rtubeY) / rtubeZ);
+
+
+				dA = projI[coordA];
+				dB = projI[coordB];
+				dC = projI[coordC];
+				dD = projI[coordD];
+
+				// Treat border of interpolated integral detector
+				if (dC == 0 && dD == 0) {
+					dC = dA;
+					dD = dB;
+				}
+
+
+				// S.3.Differentiation - Eq. 24 - Liu et al(2017)
+				pProj[(nDetX*nDetY*p) + (x * nDetY) + y] += ((dD - dC - dB + dA)*(dx*dy*dz / (cos(alpha)*cos(gamma)*detMapSizeX*detMapSizeY)));
+			}
+	}
+	return;
+}

+ 113 - 0
Functions/Sources/projectionDDb_mex/projectionDDb_mex.h

@@ -0,0 +1,113 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: March, 2019
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This is the header function
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Projection Branchless Distance Driven Code (CPU-Multithread)
+*/
+
+//typedef double user_dataType;
+//typedef float user_dataType;
+//#define user_mexdataType mxDOUBLE_CLASS
+//#define user_mexdataType mxSINGLE_CLASS
+
+#include "mex.h"
+#include <omp.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <time.h>
+
+// Global variable
+int nThreads;
+
+void projectionDDb(double* const pProj,
+	double* const pVolume,
+	double* const pTubeAngle,
+	double* const pDetAngle,
+	const  int nProj,
+	const  int nPixX,
+	const  int nPixY,
+	const  int nSlices,
+	const  int nDetX,
+	const  int nDetY,
+	const double dx,
+	const double dy,
+	const double dz,
+	const double du,
+	const double dv,
+	const double DSD,
+	const double DDR,
+	const double DAG);
+
+void mapBoudaries(double* pBound,
+	const int nElem,
+	const double valueLeftBound,
+	const double sizeElem,
+	const double offset);
+
+void mapDet2Slice(double* const pXmapp,
+	double* const pYmapp,
+	double tubeX,
+	double tubeY,
+	double tubeZ,
+	double * const pXcoord,
+	double * const pYcoord,
+	double * const pZcoord,
+	double ZSlicecoord,
+	const int nXelem,
+	const int nYelem);
+
+void bilinear_interpolation(double* projI,
+	double* pVolume,
+	double* pDetmX,
+	double* pDetmY,
+	const int nDetXMap,
+	const int nDetYMap,
+	const int nPixXMap,
+	const int nPixYMap,
+	const double pixelSize,
+	const unsigned int nz);
+
+void differentiation(double* pProj,
+	double* projI,
+	double* const pDetmX,
+	double* const pDetmY,
+	double tubeX,
+	double rtubeY,
+	double rtubeZ,
+	double* const pDetX,
+	double* const pRdetY,
+	double* const pRdetZ,
+	const int nDetX,
+	const int nDetY,
+	const int nDetXMap,
+	const int nDetYMap,
+	const double du,
+	const double dv,
+	const double dx,
+	const double dy,
+	const double dz,
+	const unsigned int p);

+ 31 - 0
Functions/Sources/projectionDDb_mex/projectionDDb_mex.sln

@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29025.244
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "projectionDDb_mex", "projectionDDb_mex.vcxproj", "{0660D5E7-4B0E-47AB-96CD-CE95BEAF90ED}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{0660D5E7-4B0E-47AB-96CD-CE95BEAF90ED}.Debug|x64.ActiveCfg = Debug|x64
+		{0660D5E7-4B0E-47AB-96CD-CE95BEAF90ED}.Debug|x64.Build.0 = Debug|x64
+		{0660D5E7-4B0E-47AB-96CD-CE95BEAF90ED}.Debug|x86.ActiveCfg = Debug|Win32
+		{0660D5E7-4B0E-47AB-96CD-CE95BEAF90ED}.Debug|x86.Build.0 = Debug|Win32
+		{0660D5E7-4B0E-47AB-96CD-CE95BEAF90ED}.Release|x64.ActiveCfg = Release|x64
+		{0660D5E7-4B0E-47AB-96CD-CE95BEAF90ED}.Release|x64.Build.0 = Release|x64
+		{0660D5E7-4B0E-47AB-96CD-CE95BEAF90ED}.Release|x86.ActiveCfg = Release|Win32
+		{0660D5E7-4B0E-47AB-96CD-CE95BEAF90ED}.Release|x86.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {F049711B-C4CE-4DB4-9737-82567E6B36DE}
+	EndGlobalSection
+EndGlobal

+ 151 - 0
Functions/Sources/projectionDDb_mex/projectionDDb_mex.vcxproj

@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="projectionDDb_mex.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="projectionDDb_mex.h" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>16.0</VCProjectVersion>
+    <ProjectGuid>{0660d5e7-4b0e-47ab-96cd-ce95beaf90ed}</ProjectGuid>
+    <RootNamespace>projectionDDb_mex</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v142</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <TargetExt>.mexw64</TargetExt>
+    <IncludePath>C:\Program Files\MATLAB\R2017b\extern\include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <TargetExt>.mexw64</TargetExt>
+    <IncludePath>C:\Program Files\MATLAB\R2017b\extern\include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+      <OpenMPSupport>true</OpenMPSupport>
+      <AdditionalOptions>/Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libut.lib;libmx.lib;libmex.lib;libmat.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalOptions>/export:mexFunction %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <ConformanceMode>true</ConformanceMode>
+      <OpenMPSupport>true</OpenMPSupport>
+      <AdditionalOptions>/Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libut.lib;libmx.lib;libmex.lib;libmat.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalOptions>/export:mexFunction %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>

+ 824 - 0
Functions/Sources/projectionDDb_mex_CUDA/kernel.cu

@@ -0,0 +1,824 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: March, 2019
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 projectionDDb_mex_CUDA(data3d,param)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function calculates the volume projection based on the
+%     Branchless Distance-Driven principle.
+%     The geometry is for DBT with half cone-beam. All parameters are set
+%     in "ParameterSettings" code.
+%
+%     INPUT:
+%
+%     - data3d = 3D volume for projection
+%     - param = Parameter of all geometry
+%	  - nProj = projection number to be projected
+%
+%     OUTPUT:
+%
+%     - proj = projections for each angle.
+%
+%     Reference:
+%     - Branchless Distance Driven Projection and Backprojection,
+%     Samit Basu and Bruno De Man (2006)
+%     - GPU Acceleration of Branchless Distance Driven Projection and
+%     Backprojection, Liu et al (2016)
+%     - GPU-Based Branchless Distance-Driven Projection and Backprojection,
+%     Liu et al (2017)
+%     - A GPU Implementation of Distance-Driven Computed Tomography,
+%     Ryan D. Wagner (2017)
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Projection Branchless Distance Driven Code
+*/
+
+#include "projectionDDb_mex_cuda.h"
+
+/****************************************************************************
+*							CUDA Kernels									*
+****************************************************************************/
+__global__ void pad_volume_kernel(double* d_img, const int nPixXMap, const int nPixYMap, const int nElem, unsigned int nz) {
+
+	const int threadGId = blockIdx.x * blockDim.x + threadIdx.x;
+
+	// Make sure we don't try and access memory outside
+	// by having any threads mapped there return early
+	if (threadGId >= nElem)
+		return;
+	
+	d_img[(nz*nPixYMap *nPixXMap) + (threadGId*nPixYMap)] = 0;
+
+	return;
+}
+
+__global__ void map_boudaries_kernel(double* d_pBound, const int nElem, const double valueLeftBound, const double sizeElem, const double offset) {
+
+	const int threadGId = blockIdx.x * blockDim.x + threadIdx.x;
+
+	// Make sure we don't try and access memory outside
+	// by having any threads mapped there return early
+	if (threadGId >= nElem)
+		return;
+
+	d_pBound[threadGId] = (threadGId - valueLeftBound) * sizeElem + offset;
+
+	return;
+}
+
+__global__ void rot_detector_kernel(double* d_pRdetY, double* d_pRdetZ, double* d_pYcoord, double* d_pZcoord, const double yOffset, const double zOffset, 
+									const double phi, const int nElem) {
+
+	const int threadGId = blockIdx.x * blockDim.x + threadIdx.x;
+
+	// Make sure we don't try and access memory outside
+	// by having any threads mapped there return early
+	if (threadGId >= nElem)
+		return;
+
+	// cos and sin are in measured in radians.
+
+	d_pRdetY[threadGId] = ((d_pYcoord[threadGId] - yOffset)* cos(phi) - (d_pZcoord[threadGId] - zOffset)* sin(phi)) + yOffset;
+	d_pRdetZ[threadGId] = ((d_pYcoord[threadGId] - yOffset)* sin(phi) + (d_pZcoord[threadGId] - zOffset)* cos(phi)) + zOffset;
+
+	return;
+
+}
+
+__global__ void mapDet2Slice_kernel(double* const pXmapp, double* const pYmapp, double tubeX, double tubeY, double tubeZ, double* const pXcoord, 
+									double* const pYcoord, double* const pZcoord, double* const pZSlicecoord, const int nDetXMap, const int nDetYMap, const unsigned int nz) {
+
+	/*
+
+	Note: Matlab has linear indexing as a column of elements, i.e, the elements are actually stored in memory as queued columns. 
+	So threads in X are launched in the column direction (DBT Y coord), and threads in Y are launched in the row direction (DBT X coord).
+
+	*/
+
+	const int2 thread_2D_pos = make_int2(blockIdx.x * blockDim.x + threadIdx.x,
+									     blockIdx.y * blockDim.y + threadIdx.y);
+
+	const int thread_1D_pos = thread_2D_pos.y * nDetYMap + thread_2D_pos.x;
+
+	// Make sure we don't try and access memory outside the detector
+	// by having any threads mapped there return early
+	if (thread_2D_pos.x >= nDetYMap || thread_2D_pos.y >= nDetXMap)
+		return;
+
+	pXmapp[thread_1D_pos] = ((pXcoord[thread_2D_pos.y] - tubeX)*(pZSlicecoord[nz] - pZcoord[thread_2D_pos.x]) - (pXcoord[thread_2D_pos.y]*tubeZ) + (pXcoord[thread_2D_pos.y]*pZcoord[thread_2D_pos.x]))/(-tubeZ + pZcoord[thread_2D_pos.x]);
+
+	if (thread_2D_pos.y == 0)
+		pYmapp[thread_2D_pos.x] = ((pYcoord[thread_2D_pos.x] - tubeY)*(pZSlicecoord[nz] - pZcoord[thread_2D_pos.x]) - (pYcoord[thread_2D_pos.x]*tubeZ) + (pYcoord[thread_2D_pos.x]*pZcoord[thread_2D_pos.x]))/(-tubeZ + pZcoord[thread_2D_pos.x]);
+
+	return;
+}
+
+__global__ void img_integration_kernel(double* d_img, const int nPixX, const int nPixY, bool direction, unsigned int offsetX, unsigned int offsetY, unsigned int nSlices) {
+
+	/*
+
+	Integration of 2D slices over the whole volume
+	
+	(S.1.Integration. - Liu et al(2017))
+
+	** Perfom an inclusive scan **
+
+	*/
+
+	const int3 memory_2D_pos = make_int3(blockIdx.x * blockDim.x + threadIdx.x + offsetX,
+										 blockIdx.y * blockDim.y + threadIdx.y + offsetY,
+										 blockIdx.z * blockDim.z + threadIdx.z);
+
+	const int2 thread_2D_pos = make_int2(blockIdx.x * blockDim.x + threadIdx.x,
+										 blockIdx.y * blockDim.y + threadIdx.y);
+
+
+	// Make sure we don't try and access memory outside the detector
+	// by having any threads mapped there return early
+	if (memory_2D_pos.x >= nPixY || memory_2D_pos.y >= nPixX || memory_2D_pos.z >= nSlices)
+		return;
+
+
+	if (direction == integrateXcoord){
+
+		for (int s = 1; s <= blockDim.y; s *= 2) {
+
+			int spot = thread_2D_pos.y - s;
+
+			double val = 0;
+
+			if (spot >= 0) {
+				val = d_img[(memory_2D_pos.z*nPixY*nPixX) + (offsetY + spot) * nPixY + memory_2D_pos.x];
+			}
+			__syncthreads();
+
+			if (spot >= 0) {
+				d_img[(memory_2D_pos.z*nPixY*nPixX) + (memory_2D_pos.y * nPixY) + memory_2D_pos.x] += val;
+			}
+			__syncthreads();
+		}
+	}
+	else
+	{
+
+		for (int s = 1; s <= blockDim.x; s *= 2) {
+
+			int spot = thread_2D_pos.x - s;
+
+			double val = 0;
+
+			if (spot >= 0) {
+				val = d_img[(memory_2D_pos.z*nPixY*nPixX) + memory_2D_pos.y * nPixY + spot + offsetX];
+			}
+			__syncthreads();
+
+			if (spot >= 0) {
+				d_img[(memory_2D_pos.z*nPixY*nPixX) + (memory_2D_pos.y * nPixY) + memory_2D_pos.x] += val;
+			}
+			__syncthreads();
+		}
+
+	}
+	return;
+}
+
+//__global__ void bilinear_interp_kernel(double* d_interpData, cudaTextureObject_t texObj, double* const d_pDetmX, double* const d_pDetmY, const int nDetXMap, const int nDetYMap, const double2 normFactor, const double2 offset)
+//{
+//
+//	const int2 thread_2D_pos = make_int2(blockIdx.x * blockDim.x + threadIdx.x,
+//										 blockIdx.y * blockDim.y + threadIdx.y);
+//
+//	// Make sure we don't try and access memory outside the detector
+//	// by having any threads mapped there return early
+//	if (thread_2D_pos.x >= nDetYMap || thread_2D_pos.y >= nDetXMap)
+//		return;
+//
+//	// Normalize coordinates from 0-1 (coords on texture memory)
+//	double xQuery = d_pDetmX[thread_2D_pos.y * nDetYMap + thread_2D_pos.x] / normFactor.x + offset.x + (-.5f * (1 / normFactor.x));
+//	double yQuery = d_pDetmY[thread_2D_pos.x] / normFactor.y + offset.y + (.5f * (1 / normFactor.y));
+//
+//	// Read from texture and write to global memory
+//	d_interpData[thread_2D_pos.y * nDetYMap + thread_2D_pos.x] = tex2D<double>(texObj, xQuery, yQuery);
+//
+//	return;
+//}
+
+__global__ void bilinear_interpolation_kernel_GPU(double* d_projI, double* d_pVolume, double* d_pDetmX, double* d_pDetmY, const int nDetXMap, const int nDetYMap,
+												  const int nPixXMap, const int nPixYMap, const int nPixX, const int nPixY, const double pixelSize, const unsigned int nz) {
+
+	const int2 thread_2D_pos = make_int2(blockIdx.x * blockDim.x + threadIdx.x,
+										 blockIdx.y * blockDim.y + threadIdx.y);
+
+	// Make sure we don't try and access memory outside the detector
+	// by having any threads mapped there return early
+	if (thread_2D_pos.x >= nDetYMap || thread_2D_pos.y >= nDetXMap)
+		return;
+
+	/*
+
+	S.2. Interpolation - Liu et al (2017)
+
+	Reference:
+	- https://en.wikipedia.org/wiki/Bilinear_interpolation
+	- https://stackoverflow.com/questions/21128731/bilinear-interpolation-in-c-c-and-cuda
+
+	Note: *** We are using the Unit Square equation ***
+
+	alpha = X - X1
+	1-alpha = X2 - X
+
+	beta = Y - Y1
+	1-beta = Y2 - Y
+
+	----> Xcoord (thread_2D_pos.y)
+	|							0___________
+	v							|_d00_|_d10_|
+	Ycoord (thread_2D_pos.x)	|_d01_|_d11_|
+
+	*/
+
+	// Adjust the mapped coordinates to cross the range of (0-nPixX).*dx 
+	//  Divide by pixelSize to get a unitary pixel size
+	const double xNormData = nPixX - d_pDetmX[thread_2D_pos.y * nDetYMap + thread_2D_pos.x] / pixelSize;
+	const signed int    xData = floor(xNormData);
+	const double  alpha = xNormData - xData;
+
+	// Adjust the mapped coordinates to cross the range of (0-nPixY).*dy  
+	//  Divide by pixelSize to get a unitary pixel size
+	const double yNormData = (nPixY / 2.0) + (d_pDetmY[thread_2D_pos.x] / pixelSize);
+	const signed int    yData = floor(yNormData);
+	const double  beta = yNormData - yData;
+
+	double d00, d01, d10, d11;
+	if (((xNormData) >= 0) && ((xNormData) <= nPixX) && ((yNormData) >= 0) && ((yNormData) <= nPixY))	d00 = d_pVolume[(nz*nPixYMap*nPixXMap) + (xData*nPixYMap + yData)];						else    d00 = 0.0;
+	if (((xData + 1) > 0) && ((xData + 1) <= nPixX) && ((yNormData) >= 0) && ((yNormData) <= nPixY))	d10 = d_pVolume[(nz*nPixYMap*nPixXMap) + ((xData + 1)*nPixYMap + yData)]; 				else    d10 = 0.0;
+	if (((xNormData) >= 0) && ((xNormData) <= nPixX) && ((yData + 1) > 0) && ((yData + 1) <= nPixY))	d01 = d_pVolume[(nz*nPixYMap*nPixXMap) + (xData*nPixYMap + yData + 1)]; 				else    d01 = 0.0;
+	if (((xData + 1) > 0) && ((xData + 1) <= nPixX) && ((yData + 1) > 0) && ((yData + 1) <= nPixY))		d11 = d_pVolume[(nz*nPixYMap*nPixXMap) + ((xData + 1)*nPixYMap + yData + 1)];			else    d11 = 0.0;
+
+	double result_temp1 = alpha * d10 + (-d00 * alpha + d00);
+	double result_temp2 = alpha * d11 + (-d01 * alpha + d01);
+
+	d_projI[thread_2D_pos.y * nDetYMap + thread_2D_pos.x] = beta * result_temp2 + (-result_temp1 * beta + result_temp1);
+
+}
+
+__global__ void differentiation_kernel(double* d_pProj, double* d_projI, double* const d_pDetmX, double* const d_pDetmY, double tubeX, double rtubeY, double rtubeZ, 
+									   double* const d_pDetX, double* const d_pRdetY, double* const d_pRdetZ, const int nDetX, const int nDetY, const int nDetXMap, 
+									   const int nDetYMap, const double du, const double dv, const double dx, const double dy,  const double dz, const unsigned int p) {
+
+	const int2 thread_2D_pos = make_int2(blockIdx.x * blockDim.x + threadIdx.x,
+										 blockIdx.y * blockDim.y + threadIdx.y);
+
+	const int thread_1D_pos = (nDetX*nDetY*p) + (thread_2D_pos.y * nDetY) + thread_2D_pos.x;
+
+	// Make sure we don't try and access memory outside the detector
+	// by having any threads mapped there return early
+	if (thread_2D_pos.x >= nDetY || thread_2D_pos.y >= nDetX)
+		return;
+
+	/*
+
+	S.3. Differentiation - Eq. 24 - Liu et al (2017)
+	
+	Detector integral projection
+	 ___________
+	|_A_|_B_|___|
+	|_C_|_D_|___|
+	|___|___|___| 
+
+
+	(thread_2D_pos.x,thread_2D_pos.y)
+	________________
+	|_A_|__B__|_____|	
+	|_C_|(0,0)|(0,1)|
+	|___|(1,0)|(1,1)|
+
+	Threads are lauched from D up to nDetX (thread_2D_pos.y) and nDetY (thread_2D_pos.x)
+	i.e., they are running on the detector image. Thread (0,0) is on D.
+
+	Coordinates on intergal projection:
+
+	A = thread_2D_pos.y * nDetYMap + thread_2D_pos.x
+	B = ((thread_2D_pos.y+1) * nDetYMap) + thread_2D_pos.x
+	C = thread_2D_pos.y * nDetYMap + thread_2D_pos.x + 1
+	D = ((thread_2D_pos.y+1) * nDetYMap) + thread_2D_pos.x + 1
+	
+	*/
+
+	unsigned int coordA = thread_2D_pos.y * nDetYMap + thread_2D_pos.x;
+	unsigned int coordB = ((thread_2D_pos.y + 1) * nDetYMap) + thread_2D_pos.x;
+	unsigned int coordC = coordA + 1;
+	unsigned int coordD = coordB + 1;
+
+
+	// Detector X coord overlap calculation
+	double detMapSizeX = d_pDetmX[coordA] - d_pDetmX[coordB];
+
+	// Detector Y coord overlap calculation
+	double detMapSizeY = d_pDetmY[thread_2D_pos.x+1] - d_pDetmY[thread_2D_pos.x];
+
+	// x - ray angle in X coord
+	double gamma = atan((d_pDetX[thread_2D_pos.y] + (du / 2.0) - tubeX) / rtubeZ);
+
+	// x - ray angle in Y coord
+	double alpha = atan((d_pRdetY[thread_2D_pos.x] + (dv / 2.0) - rtubeY) / rtubeZ);
+
+
+	double dA, dB, dC, dD;
+
+	dA = d_projI[coordA];
+	dB = d_projI[coordB];
+	dC = d_projI[coordC];
+	dD = d_projI[coordD];
+
+	// Treat border of interpolated integral detector
+	if (dC == 0 && dD == 0) {
+		dC = dA;
+		dD = dB;
+	}
+
+
+	// S.3.Differentiation - Eq. 24 - Liu et al(2017)
+	d_pProj[thread_1D_pos] += ((dD - dC - dB + dA)*(dx*dy*dz / (cos(alpha)*cos(gamma)*detMapSizeX*detMapSizeY)));
+
+	return;
+}
+
+
+
+/****************************************************************************
+* function: projectionDDb() - CUDA projection Branchless Distance Driven.	*
+****************************************************************************/
+
+void projectionDDb(double* const h_pProj,
+	double* const h_pVolume,
+	double* const h_pTubeAngle,
+	double* const h_pDetAngle,
+	const double idXProj,
+	const unsigned int nProj,
+	const unsigned int nPixX,
+	const unsigned int nPixY,
+	const unsigned int nSlices,
+	const unsigned int nDetX,
+	const unsigned int nDetY,
+	const double dx,
+	const double dy,
+	const double dz,
+	const double du,
+	const double dv,
+	const double DSD,
+	const double DDR,
+	const double DAG)
+{
+
+	// Unique GPU
+	int devID = 0;
+
+	cudaDeviceProp deviceProp;
+	cudaGetDeviceProperties(&deviceProp, devID);
+	cudaCheckErrors("cudaGetDeviceProperties");
+
+	const unsigned int maxThreadsPerBlock = deviceProp.maxThreadsPerBlock;
+
+
+	mexPrintf("GPU Device %d: \"%s\" with compute capability %d.%d has %d Multi-Processors and %zu bytes of global memory\n\n", devID,
+		deviceProp.name, deviceProp.major, deviceProp.minor, deviceProp.multiProcessorCount, deviceProp.totalGlobalMem);
+
+
+	// Create timmer variables and start it
+	StopWatchInterface *timer = NULL;
+	sdkCreateTimer(&timer);
+	sdkStartTimer(&timer);
+
+	//cudaStream_t stream1;
+	//cudaStreamCreate(&stream1);
+
+	dim3 threadsPerBlock(1, 1, 1);
+	dim3 blockSize(1, 1, 1);
+
+
+	// Number of mapped detector and pixels
+	const int nDetXMap = nDetX + 1;
+	const int nDetYMap = nDetY + 1;
+	const int nPixXMap = nPixX + 1;
+	const int nPixYMap = nPixY + 1;
+
+	
+	// Convention: h_ variables live on host
+	// Convention: d_ variables live on device (GPU global mem)
+	double* d_pProj;
+	double* d_projI;
+	double* d_pVolume;
+	double* d_pTubeAngle;
+	double* d_pDetAngle;
+
+
+	// Allocate global memory on the device, place result in "d_----"
+	cudaMalloc((void **)&d_pProj, nDetX*nDetY*nProj * sizeof(double));
+	cudaMalloc((void **)&d_projI, nDetXMap*nDetYMap * sizeof(double));
+	cudaMalloc((void **)&d_pVolume, nPixXMap*nPixYMap*nSlices * sizeof(double));
+	cudaMalloc((void **)&d_pTubeAngle, nProj * sizeof(double));
+	cudaMalloc((void **)&d_pDetAngle, nProj * sizeof(double));
+
+	cudaCheckErrors("cudaMalloc Initial");
+
+	// Copy data from host memory "h_----" to device memory "d_----"
+	cudaMemcpy((void *)d_pTubeAngle, (void *)h_pTubeAngle, nProj * sizeof(double), cudaMemcpyHostToDevice);
+	cudaMemcpy((void *)d_pDetAngle, (void *)h_pDetAngle, nProj * sizeof(double), cudaMemcpyHostToDevice);
+
+	cudaCheckErrors("cudaMemcpy Angles");
+
+
+	/*
+		Copy volume data from host memory "h_----" to device memory "d_----" padding with zeros for image integation
+	*/
+
+
+	// Initialize first column and row with zeros
+	double* h_pVolume_tmp;
+	double* d_pVolume_tmp;
+
+	threadsPerBlock.x = maxThreadsPerBlock;
+	blockSize.x = (nPixXMap / maxThreadsPerBlock) + 1;
+
+	for (int nz = 0; nz < nSlices; nz++) {
+
+		// Pad on X coord direction
+		pad_volume_kernel << <blockSize, threadsPerBlock >> > (d_pVolume, nPixXMap, nPixYMap, nPixXMap, nz);
+
+		// Pad on Y coord direction
+		d_pVolume_tmp = d_pVolume + (nPixXMap*nPixYMap*nz) + 1;
+		cudaMemset(d_pVolume_tmp, 0, nPixY * sizeof(double));
+		cudaCheckErrors("cudaMemset Padding Volume");
+	}
+
+
+	// Copy volume data from host memory
+	for (unsigned int nz = 0; nz < nSlices; nz++)
+		for (unsigned int c = 0; c < nPixX; c++) {
+
+			h_pVolume_tmp = h_pVolume + (c *nPixY) + (nPixX*nPixY*nz);
+			d_pVolume_tmp = d_pVolume + (((c+1) *nPixYMap) + 1) + (nPixXMap*nPixYMap*nz);
+
+			cudaMemcpy((void *)d_pVolume_tmp, (void *)h_pVolume_tmp, nPixY * sizeof(double), cudaMemcpyHostToDevice);
+			cudaCheckErrors("cudaMemset Volume");
+		}
+
+	
+
+	// Pointer for projections coordinates
+	double* d_pDetX;
+	double* d_pDetY;
+	double* d_pDetZ;
+	double* d_pObjX;
+	double* d_pObjY;
+	double* d_pObjZ;
+
+	// Allocate global memory on the device for projections coordinates
+	cudaMalloc((void **)&d_pDetX, nDetXMap * sizeof(double));
+	cudaMalloc((void **)&d_pDetY, nDetYMap * sizeof(double));
+	cudaMalloc((void **)&d_pDetZ, nDetYMap * sizeof(double));
+	cudaMalloc((void **)&d_pObjX, nPixXMap * sizeof(double));
+	cudaMalloc((void **)&d_pObjY, nPixYMap * sizeof(double));
+	cudaMalloc((void **)&d_pObjZ, nSlices * sizeof(double));
+
+	cudaCheckErrors("cudaMalloc Coordinates");
+
+
+
+	// Pointer for mapped coordinates
+	double* d_pDetmY;
+	double* d_pDetmX;
+
+
+	// Allocate global memory on the device for mapped coordinates
+	cudaMalloc((void **)&d_pDetmY, nDetYMap * sizeof(double));
+	cudaMalloc((void **)&d_pDetmX, nDetYMap * nDetXMap * sizeof(double));
+
+	cudaCheckErrors("cudaMalloc Map-Coordinates");
+
+
+	// Pointer for rotated detector coords
+	double* d_pRdetY;
+	double* d_pRdetZ;
+
+	// Allocate global memory on the device for for rotated detector coords
+	cudaMalloc((void **)&d_pRdetY, nDetYMap * sizeof(double));
+	cudaMalloc((void **)&d_pRdetZ, nDetYMap * sizeof(double));
+
+	cudaCheckErrors("cudaMalloc Rot-Coordinates");
+
+	// Generate detector and object boudaries
+
+	threadsPerBlock.x = maxThreadsPerBlock;
+
+	blockSize.x = (nDetX / maxThreadsPerBlock) + 1;
+
+	map_boudaries_kernel << <blockSize, threadsPerBlock >> > (d_pDetX, nDetXMap, (double)nDetX, -du, 0.0);
+
+	blockSize.x = (nDetY / maxThreadsPerBlock) + 1;
+
+	map_boudaries_kernel << <blockSize, threadsPerBlock >> > (d_pDetY, nDetYMap, nDetY / 2.0, dv, 0.0);
+
+	blockSize.x = (nPixX / maxThreadsPerBlock) + 1;
+
+	map_boudaries_kernel << <blockSize, threadsPerBlock >> > (d_pObjX, nPixXMap, (double)nPixX, -dx, 0.0);
+
+	blockSize.x = (nPixY / maxThreadsPerBlock) + 1;
+
+	map_boudaries_kernel << <blockSize, threadsPerBlock >> > (d_pObjY, nPixYMap, nPixY / 2.0, dy, 0.0);
+
+	blockSize.x = (nSlices / maxThreadsPerBlock) + 1;
+
+	map_boudaries_kernel << <blockSize, threadsPerBlock >> > (d_pObjZ, nSlices, 0.0, dz, DAG + (dz / 2.0));
+
+
+	//mexPrintf("Map Boundaries -- Threads:%d Blocks:%d \n", threads, blocks);
+
+
+	// Initiate variables value with 0
+	cudaMemset(d_pDetZ, 0, nDetYMap * sizeof(double));
+	cudaMemset(d_pProj, 0, nDetX * nDetY * nProj * sizeof(double));
+
+	cudaCheckErrors("cudaMemset Zeros");
+
+	//cudaDeviceSynchronize();
+
+	// X - ray tube initial position
+	double tubeX = 0;
+	double tubeY = 0;
+	double tubeZ = DSD;
+
+	// Iso - center position
+	double isoY = 0;
+	double isoZ = DDR;
+
+
+	// Integration of 2D slices over the whole volume
+	// (S.1.Integration. - Liu et al(2017))
+		
+	// Naive integration o the X coord
+	threadsPerBlock.x = 10;
+	threadsPerBlock.y = 10;
+	threadsPerBlock.z = 10;
+
+	blockSize.x = (unsigned int) ceil(((double)nPixYMap / (threadsPerBlock.x-1)));
+	blockSize.y = 1;
+	blockSize.z = (unsigned int) ceil((double)nSlices / threadsPerBlock.z);
+
+	//mexPrintf("Divisao: %f \n", (double)nPixY / (threadsPerBlock.x - 1));
+	//mexPrintf("Divisao ceil: %d \n", (unsigned int)ceil((double)nPixY / (threadsPerBlock.x - 1)));
+
+	for (int k = 0; k < ceil((double)nPixXMap / (threadsPerBlock.x-1)); k++) {
+
+		img_integration_kernel << <blockSize, threadsPerBlock >> > (d_pVolume, nPixXMap, nPixYMap, integrateXcoord, 0, k * 9, nSlices);
+
+		//mexPrintf("integration kernel -- threadsX:%d blocksX:%d threadsY:%d blocksY:%d \n", threadsPerBlock.x, blockSize.x, threadsPerBlock.y, blockSize.y);
+
+	}
+
+	// Naive integration o the Y coord
+	threadsPerBlock.x = 10;
+	threadsPerBlock.y = 10;
+	threadsPerBlock.z = 10;
+
+	blockSize.x = 1;
+	blockSize.y = (unsigned int) ceil((double)nPixXMap / (threadsPerBlock.y-1));
+	blockSize.z = (unsigned int) ceil((double)nSlices / threadsPerBlock.z);
+
+
+	for (int k = 0; k < ceil((double)nPixYMap / (threadsPerBlock.y-1)); k++) {
+
+		img_integration_kernel << <blockSize, threadsPerBlock >> > (d_pVolume, nPixXMap, nPixYMap, integrateYcoord, k * 9, 0, nSlices);
+
+		//mexPrintf("integration kernel -- threadsX:%d blocksX:%d threadsY:%d blocksY:%d \n", threadsPerBlock.x, blockSize.x, threadsPerBlock.y, blockSize.y);
+
+	}
+
+	// Test if we will loop over all projs or not
+	unsigned int projIni, projEnd;
+	if(idXProj == -1){
+		projIni = 0;
+		projEnd = nProj;
+	}
+	else{
+		projIni = (unsigned int) idXProj;
+		projEnd = (unsigned int) idXProj + 1;
+	}
+
+	// For each projection
+	for (unsigned int p = projIni; p < projEnd; p++) {
+		
+		// Get specif tube angle for the projection
+		double theta = h_pTubeAngle[p] * M_PI / 180.0;
+
+		// Get specif detector angle for the projection
+		double phi = h_pDetAngle[p] * M_PI / 180.0;
+
+		//mexPrintf("Tube angle:%f Det angle:%f\n", theta, phi);
+
+		// Tube rotation
+		double rtubeY = ((tubeY - isoY)*cos(theta) - (tubeZ - isoZ)*sin(theta)) + isoY;
+		double rtubeZ = ((tubeY - isoY)*sin(theta) + (tubeZ - isoZ)*cos(theta)) + isoZ;
+
+		//mexPrintf("R tube Y:%f R tube Z:%f\n", rtubeY, rtubeZ);
+
+		// Detector rotation
+		threadsPerBlock.x = maxThreadsPerBlock;
+		threadsPerBlock.y = 1;
+		threadsPerBlock.z = 1;
+
+		blockSize.x = (nDetYMap / maxThreadsPerBlock) + 1;
+		blockSize.y = 1;
+		blockSize.z = 1;
+
+		rot_detector_kernel << <blockSize, threadsPerBlock >> > (d_pRdetY, d_pRdetZ, d_pDetY, d_pDetZ, isoY, isoZ, phi, nDetYMap);
+
+		//cudaDeviceSynchronize();
+
+		//mexPrintf("Detector rotation -- Threads:%d Blocks:%d \n", threads, blocks);
+
+
+		// For each slice
+		for (unsigned int nz = 0; nz < nSlices; nz++) {
+
+			/*
+
+			Map detector onto XY plane(Inside proj loop in case detector rotates)
+
+			*** Note: Matlab has linear indexing as a column of elements, i.e, the elements are actually stored in memory as queued columns.
+			So threads in X are launched in the column direction (DBT Y coord), and threads in Y are launched in the row direction (DBT X coord).
+
+			*/
+
+			threadsPerBlock.x = 32;
+			threadsPerBlock.y = 32;
+			threadsPerBlock.z = 1;
+
+			blockSize.x = (nDetYMap / threadsPerBlock.x) + 1;
+			blockSize.y = (nDetXMap / threadsPerBlock.y) + 1;
+			blockSize.z = 1;
+
+
+			mapDet2Slice_kernel << <blockSize, threadsPerBlock >> > (d_pDetmX, d_pDetmY, tubeX, rtubeY, rtubeZ, d_pDetX, d_pRdetY, d_pRdetZ, d_pObjZ, nDetXMap, nDetYMap, nz);
+			//cudaDeviceSynchronize();
+
+			//mexPrintf("Map detector onto XY plane -- ThreadsX:%d ThreadsY:%d BlocksX:%d BlocksY:%d\n", threadsPerBlock.x, threadsPerBlock.y, blockSizeX, blockSizeY);
+			
+			/*
+			Creates texture memory
+			*/
+
+			/*
+
+			// Allocate CUDA array in device memory
+			cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc(32, 0, 0, 0,cudaChannelFormatKindFloat);
+
+			cudaArray* d_imageArray;
+			cudaMallocArray(&d_imageArray, &channelDesc, nPixXMap, nPixYMap);
+				
+			// Copy to texture memory			
+			checkCudaErrors(cudaMemcpyToArray(d_imageArray, 0, 0, d_pSlice, nPixXMap* nPixYMap * sizeof(double), cudaMemcpyDeviceToDevice));
+
+			// Specify texture
+			struct cudaResourceDesc resDesc;
+			memset(&resDesc, 0, sizeof(resDesc));
+			resDesc.resType = cudaResourceTypeArray;
+			resDesc.res.array.array = d_imageArray;
+
+			// Specify texture object parameters
+			struct cudaTextureDesc texDesc;
+			memset(&texDesc, 0, sizeof(texDesc));
+			texDesc.addressMode[0] = cudaAddressModeBorder;
+			texDesc.addressMode[1] = cudaAddressModeBorder;
+			texDesc.filterMode = cudaFilterModeLinear;
+			texDesc.readMode = cudaReadModeElementType;
+			texDesc.normalizedCoords = true;
+
+			// Create texture object
+			cudaTextureObject_t texObj = 0;
+			cudaCreateTextureObject(&texObj, &resDesc, &texDesc, NULL);
+
+			// Allocate result of transformation in device memory
+			double* d_projI;
+			cudaMalloc(&d_projI, nDetXMap*nDetYMap * sizeof(double));
+
+			// Invoke kernel
+			blockSize.x = (nDetYMap / threadsPerBlock.x) + 1;
+			blockSize.y = (nDetXMap / threadsPerBlock.y) + 1;
+
+			const double2 normFactor = make_double2(nPixX*-dx,
+												  nPixY*dy);
+
+			const double2 offset = make_double2(1,
+											  0.5);
+
+			bilinear_interp_kernel << <blockSize, threadsPerBlock >> > (d_projI, texObj, d_pDetmX, d_pDetmY, nDetXMap, nDetYMap, normFactor, offset);
+
+			mexPrintf("Texture interpolation -- Threads:%d Blocks:%d \n", threads, blocks);
+
+			 Destroy texture object
+			cudaDestroyTextureObject(texObj);
+			*/
+
+
+			/*
+				S.2. Interpolation - Liu et al (2017)
+			*/
+
+			blockSize.x = (nDetYMap / threadsPerBlock.x) + 1;
+			blockSize.y = (nDetXMap / threadsPerBlock.y) + 1;
+
+			bilinear_interpolation_kernel_GPU << <blockSize, threadsPerBlock >> > (d_projI, d_pVolume, d_pDetmX, d_pDetmY, nDetXMap, nDetYMap, nPixXMap, nPixYMap, nPixX, nPixY, dx, nz);
+
+
+			/*
+				S.3. Differentiation - Eq. 24 - Liu et al (2017)
+			*/
+
+			blockSize.x = (nDetY / threadsPerBlock.x) + 1;
+			blockSize.y = (nDetX / threadsPerBlock.y) + 1;
+
+			differentiation_kernel << <blockSize, threadsPerBlock >> > (d_pProj, d_projI, d_pDetmX, d_pDetmY, tubeX, rtubeY, rtubeZ, d_pDetX, d_pRdetY, d_pRdetZ, nDetX, nDetY, nDetXMap, nDetYMap, du, dv, dx, dy, dz, p);
+
+		} // Loop end slices
+
+	} // Loop end Projections
+
+
+	//nDetYMap
+	//double* const h_pPixmX = (double*)mxMalloc(nDetXMap*nDetYMap * sizeof(double));
+	//double* const h_pPixmY = (double*)mxMalloc(nDetYMap * sizeof(double));
+	//cudaMemcpy((void *)h_pPixmX, (void *)d_pDetmX, nDetXMap*nDetYMap * sizeof(double), cudaMemcpyDeviceToHost);
+	//cudaMemcpy((void *)h_pPixmY, (void *)d_pDetmY, nDetYMap * sizeof(double), cudaMemcpyDeviceToHost);
+
+	//for (int u = 0; u < nDetXMap; u++)
+	//	for (int v = 0; v < nDetYMap; v++) {
+	//		h_pProj[(0 * nDetYMap*nDetXMap) + (u*nDetYMap) + v] = h_pPixmX[(u*nDetYMap) + v];
+	//		h_pProj[(1 * nDetYMap*nDetXMap) + (u*nDetYMap) + v] = h_pPixmY[v];
+	//	}
+	
+	// d_projI
+	//double* const h_pPixmX = (double*)mxMalloc(nDetXMap*nDetYMap * sizeof(double));
+	//cudaMemcpy((void *)h_pPixmX, (void *)d_projI, nDetXMap*nDetYMap * sizeof(double), cudaMemcpyDeviceToHost);
+
+	//for (int u = 0; u < nDetXMap; u++)
+	//	for (int v = 0; v < nDetYMap; v++) {
+	//		h_pProj[(0 * nDetYMap*nDetXMap) + (u*nDetYMap) + v] = h_pPixmX[(u*nDetYMap) + v];
+	//	}
+	
+
+	// d_pVolume 
+	//cudaMemcpy((void *)h_pProj, (void *)d_pVolume, nSlices*nPixXMap*nPixYMap * sizeof(double), cudaMemcpyDeviceToHost);
+
+	// d_pProj
+	cudaMemcpy((void *)h_pProj, (void *)d_pProj, nProj*	nDetX * nDetY * sizeof(double), cudaMemcpyDeviceToHost);
+	cudaCheckErrors("cudaMemcpy Final");
+
+
+	cudaFree(d_pProj);
+	cudaFree(d_projI);
+	cudaFree(d_pVolume);
+	cudaFree(d_pTubeAngle);
+	cudaFree(d_pDetAngle);
+	cudaFree(d_pDetX);
+	cudaFree(d_pDetY);
+	cudaFree(d_pDetZ);
+	cudaFree(d_pObjX);
+	cudaFree(d_pObjY);
+	cudaFree(d_pObjZ);
+	cudaFree(d_pDetmY);
+	cudaFree(d_pDetmX);
+	cudaFree(d_pRdetY);
+	cudaFree(d_pRdetZ);
+
+	cudaCheckErrors("cudaFree Final");
+
+	sdkStopTimer(&timer);
+	mexPrintf("Processing time: %f (ms)\n", sdkGetTimerValue(&timer));
+	sdkDeleteTimer(&timer);
+
+	cudaDeviceReset();
+
+	return;
+
+}

+ 25 - 0
Functions/Sources/projectionDDb_mex_CUDA/projectionDDb_mex_CUDA.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29025.244
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "projectionDDb_mex_CUDA", "projectionDDb_mex_CUDA.vcxproj", "{BFB74CBA-38F0-47C2-B592-1DC6D240F060}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x64 = Debug|x64
+		Release|x64 = Release|x64
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{BFB74CBA-38F0-47C2-B592-1DC6D240F060}.Debug|x64.ActiveCfg = Debug|x64
+		{BFB74CBA-38F0-47C2-B592-1DC6D240F060}.Debug|x64.Build.0 = Debug|x64
+		{BFB74CBA-38F0-47C2-B592-1DC6D240F060}.Release|x64.ActiveCfg = Release|x64
+		{BFB74CBA-38F0-47C2-B592-1DC6D240F060}.Release|x64.Build.0 = Release|x64
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {283E7AFF-8F69-4937-AA6F-2C3D9A5B73DE}
+	EndGlobalSection
+EndGlobal

+ 103 - 0
Functions/Sources/projectionDDb_mex_CUDA/projectionDDb_mex_CUDA.vcxproj

@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <CudaCompile Include="kernel.cu" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="projectionDDb_mex_cuda.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="projectionDDb_mex_cuda.h" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{bfb74cba-38f0-47c2-b592-1dc6d240f060}</ProjectGuid>
+    <RootNamespace>projectionDDb_mex_CUDA</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v142</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+    <PlatformToolset>v142</PlatformToolset>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+    <Import Project="$(VCTargetsPath)\BuildCustomizations\CUDA 10.1.props" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <TargetExt>.mexw64</TargetExt>
+    <IncludePath>C:\ProgramData\NVIDIA Corporation\CUDA Samples\v10.1\common\inc;C:\Program Files\MATLAB\R2017b\extern\include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <TargetExt>.mexw64</TargetExt>
+    <IncludePath>C:\ProgramData\NVIDIA Corporation\CUDA Samples\v10.1\common\inc;C:\Program Files\MATLAB\R2017b\extern\include;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>libut.lib;libmx.lib;libmex.lib;libmat.lib;cudart_static.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalOptions>/export:mexFunction %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+    <CudaCompile>
+      <TargetMachinePlatform>64</TargetMachinePlatform>
+    </CudaCompile>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <SubSystem>Console</SubSystem>
+      <AdditionalDependencies>libut.lib;libmx.lib;libmex.lib;libmat.lib;cudart_static.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>C:\Program Files\MATLAB\R2017b\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalOptions>/export:mexFunction %(AdditionalOptions)</AdditionalOptions>
+    </Link>
+    <CudaCompile>
+      <TargetMachinePlatform>64</TargetMachinePlatform>
+    </CudaCompile>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+    <Import Project="$(VCTargetsPath)\BuildCustomizations\CUDA 10.1.targets" />
+  </ImportGroup>
+</Project>

+ 163 - 0
Functions/Sources/projectionDDb_mex_CUDA/projectionDDb_mex_cuda.cpp

@@ -0,0 +1,163 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: March, 2019
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 projectionDDb_mex_CUDA(data3d,param)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function calculates the volume projection based on the
+%     Branchless Distance-Driven principle.
+%     The geometry is for DBT with half cone-beam. All parameters are set
+%     in "ParameterSettings" code.
+%
+%     INPUT:
+%
+%     - data3d = 3D volume for projection
+%     - param = Parameter of all geometry
+%	  - nProj = projection number to be projected
+%
+%     OUTPUT:
+%
+%     - proj = projections for each angle.
+%
+%     Reference:
+%     - Branchless Distance Driven Projection and Backprojection,
+%     Samit Basu and Bruno De Man (2006)
+%     - GPU Acceleration of Branchless Distance Driven Projection and
+%     Backprojection, Liu et al (2016)
+%     - GPU-Based Branchless Distance-Driven Projection and Backprojection,
+%     Liu et al (2017)
+%     - A GPU Implementation of Distance-Driven Computed Tomography,
+%     Ryan D. Wagner (2017)
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Projection Branchless Distance Driven Code
+*/
+
+#include "projectionDDb_mex_cuda.h"
+
+/****************************************************************************
+* function: mexFunction() - Matlab interface function.						*
+* INPUTS:																	*
+*   nlhs - Number of expected output mxArrays, specified as an integer.		*
+*   plhs[] - Array of pointers to the expected mxArray output arguments.	*
+*   nrhs - Number of input mxArrays, specified as an integer.				*
+*   prhs[] - Array of pointers to the mxArray input arguments.				*
+****************************************************************************/
+
+void mexFunction(int nlhs, mxArray *plhs[],
+	int nrhs, const mxArray *prhs[]) {
+
+
+	/* Check for proper number of arguments */
+	if (nrhs != 3) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:nargin",
+			"projection_mex requires three input arguments.");
+	}
+	if (nlhs != 1) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:nargout",
+			"projection_mex requires one output argument.");
+	}
+
+	/* Check if the input is of proper type */
+	if (!mxIsDouble(prhs[0])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument has to be double.");
+	}
+	if (!mxIsStruct(prhs[1])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"Second argument has to be a struct.");
+	}
+	if (!mxIsDouble(prhs[2])) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"Third argument has to be double.");
+	}
+
+	double* const pVolume = (double*)(mxGetPr(prhs[0])); 
+	double* const pTubeAngleD = mxGetPr(mxGetField(prhs[1], 0, "tubeDeg"));
+	double* const pDetAngleD = mxGetPr(mxGetField(prhs[1], 0, "detectorDeg"));
+
+	
+	const int unsigned nProj = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nProj"));
+
+	const int unsigned nPixX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nx"));
+	const int unsigned nPixY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "ny"));
+	const int unsigned nSlices = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nz"));
+	const int unsigned nDetX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nu"));
+	const int unsigned nDetY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nv"));
+
+	const double dx = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dx"));
+	const double dy = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dy"));
+	const double dz = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dz"));
+	const double du = (const double)mxGetScalar(mxGetField(prhs[1], 0, "du"));
+	const double dv = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dv"));
+
+	const double DSD = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DSD"));
+	const double DDR = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DDR"));
+	const double DAG = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DAG"));
+
+	const double idXProj = (const double)mxGetScalar(prhs[2]);
+
+	mwSize NRow = mxGetM(prhs[0]);
+	mwSize NCol = mxGetN(prhs[0]);
+	mwSize NElemVol = mxGetNumberOfElements(prhs[0]);
+
+	/* Check if the input is in proper size */
+	if (NRow != nPixY) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument needs to have the same number of rows as in the configuration file.");
+	}
+
+	if (NCol/nSlices != nPixX) {
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"First argument needs to have the same number of columns as in the configuration file.");
+	}
+
+	if (idXProj >= nProj ){
+		mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
+			"Third argument needs to be less than the proj number (0->nproj-1)");
+	}
+
+	// Cast double vectors to single 
+	double* const pTubeAngle = (double*)mxMalloc(nProj * sizeof(double));
+	double* const pDetAngle = (double*)mxMalloc(nProj * sizeof(double));
+
+	for (unsigned int n = 0; n < nProj; n++) {
+		pTubeAngle[n] = (double)pTubeAngleD[n];
+		pDetAngle[n] = (double)pDetAngleD[n];
+	}
+
+	//mexPrintf("Nx:%d Ny:%d Nz:%d \nNu:%d Nv:%d \nDx:%.2f Dy:%.2f Dz:%.2f \nDu:%.2f Dv:%.2f \nDSD:%.2f DDR:%.2f \n", nPixX, nPixY, nSlices, nDetX, nDetY, dx, dy, dz, du, dv, DSD, DDR);
+
+
+	// Output memory allocation
+	mwSize dims[3] = { nDetY,nDetX,nProj };
+	plhs[0] = mxCreateNumericArray(3, dims, mxDOUBLE_CLASS, mxREAL);
+	double* const pProj = (double*)mxGetData(plhs[0]);
+
+	projectionDDb(pProj, pVolume, pTubeAngle, pDetAngle, idXProj, nProj, nPixX, nPixY, nSlices, nDetX, nDetY, dx, dy, dz, du, dv, DSD, DDR, DAG);
+
+	mxFree(pTubeAngle);
+	mxFree(pDetAngle);
+
+	return;
+
+}

+ 69 - 0
Functions/Sources/projectionDDb_mex_CUDA/projectionDDb_mex_cuda.h

@@ -0,0 +1,69 @@
+/*
+%% Author: Rodrigo de Barros Vimieiro
+% Date: March, 2019
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This is the header function
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+%
+%     This program 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.
+%
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Projection Branchless Distance Driven header
+*/
+
+//typedef double user_dataType;
+//typedef float user_dataType;
+//#define user_mexdataType mxDOUBLE_CLASS
+//#define user_mexdataType mxSINGLE_CLASS
+
+#include "mex.h"
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+// Includes CUDA
+#include <cuda_runtime.h>
+
+// Utilities and timing functions
+#include <helper_functions.h>    // includes cuda.h and cuda_runtime_api.h
+
+// CUDA helper functions
+#include <helper_cuda.h>         // helper functions for CUDA error check
+
+#define integrateXcoord 1
+#define integrateYcoord 0
+
+
+/*
+Reference: TIGRE - https://github.com/CERN/TIGRE
+*/
+#define cudaCheckErrors(msg) \
+do { \
+        cudaError_t __err = cudaGetLastError(); \
+        if (__err != cudaSuccess) { \
+                mexPrintf("%s \n",msg);\
+                mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",cudaGetErrorString(__err));\
+        } \
+} while (0)
+
+
+void projectionDDb(double* const h_pProj, double* const h_pVolume, double* const h_pTubeAngle, double* const h_pDetAngle, const double idXProj, const unsigned int nProj, const unsigned int nPixX,
+	const unsigned int nPixY, const unsigned int nSlices, const unsigned int nDetX, const unsigned int nDetY, const double dx, const double dy, double dz, const double du,
+	const double dv, const double DSD, const double DDR, const double DAG);

+ 125 - 0
Functions/backprojection.m

@@ -0,0 +1,125 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: April, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 backprojection(proj,param,projNumber)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function makes the simple backprojection of 2D images, in order 
+%     to reconstruct a certain number of slices.
+%  
+%     The function calculates for each voxel, which detector pixel is
+%     associated with that voxel in the specific projection. That is done 
+%     for all angles specified.
+%     
+%     The geometry is for DBT with half cone-beam. All parameters are set 
+%     in "ParameterSettings" code. 
+%  
+%     INPUT:
+% 
+%     - proj = 2D projection images 
+%     - param = Parameter of all geometry
+%     - projNumber = Vector with projections numbers to be processed
+% 
+%     OUTPUT:
+% 
+%     - data3d = Volume data.
+% 
+%     Reference: Patent US5872828
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% Backprojection Code
+function data3d = backprojection(proj,param,projNumber)
+
+% Get parameters from struct
+DSR = param.DSR;
+DDR = param.DDR;
+tubeAngle = deg2rad(param.tubeDeg);
+numVPixels = param.nv;
+numUPixels = param.nu;
+numSlices = param.nz;
+numProjs = param.nProj;
+zCoords = param.zs;         % Z object coordinates
+
+% Test if there's specific angles
+if(isempty(projNumber))
+    projNumber = 1:numProjs;
+else
+    if(max(projNumber(:)) <= numProjs)
+        numProjs = size(projNumber,2);
+    else
+        error('Projection number exceeds the maximum for the equipment.')
+    end
+end
+
+% Stack of reconstructed slices
+data3d = zeros(param.ny, param.nx, param.nz,'single');
+
+% Object Coordinate system in (mm)
+[xCoord,yCoord] = meshgrid(param.xs,param.ys);
+
+fprintf('%2d/%2d', 1, numProjs)
+
+% For each projection
+for p=1:numProjs
+
+    fprintf('\b\b\b\b\b%2d/%2d', p, numProjs)
+    
+    % Get specific projection number
+    projN = projNumber(p);
+    
+    % Get specific angle for the backprojection
+    teta = tubeAngle(projN);
+    
+    % Get specif projection from stack
+    proj_tmp = proj(:,:,p);
+    
+    % For each slice
+    for nz=1:numSlices
+
+        % Calculates a relation of voxel and detector coordinates
+        pvCoord = yCoord+((zCoords(nz).*((DSR.*sin(teta))+ yCoord))./...
+                        ((DSR.*cos(teta))+ DDR -zCoords(nz)));
+                
+        puCoord = (xCoord.*((DSR.*cos(teta))+DDR))./ ...
+                 ((DSR.*cos(teta))+DDR-zCoords(nz));
+                
+        % Coordinate in Pixel of detector plane axis origin
+        u0 = numUPixels;
+        v0 = numVPixels/2;
+        
+        % Represent detector plane axis (Xi,Yi) in the image plane axis (i,j) (covert mm to Pixels)
+        puCoord = -(puCoord./param.du) + u0;
+        pvCoord =  (pvCoord./param.dv) + v0;      
+        
+        % Interpolation of the pixel coordinates of the "Projection" at the calculated pixel coordinates for the slices
+        slice_tmp = interp2(proj_tmp,puCoord,pvCoord,'linear', 0);       
+        
+        % Acumulate current slice with slice from previus projection
+        data3d(:,:,nz) = data3d(:,:,nz) + slice_tmp;
+        
+    end % Loop end slices 
+    
+end % Loop end Projections
+
+data3d(:) = data3d(:)./ numProjs;
+
+end

+ 334 - 0
Functions/backprojectionDD.m

@@ -0,0 +1,334 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: July, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 backprojectionDD(proj,param)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function reconstruct the 3D volume from projections, based on 
+%     the Distance-Driven principle. It works by calculating the overlap  
+%     in X and Y axis of the volume and the detector boundaries.  
+%     The geometry is for DBT with half cone-beam. All parameters are set 
+%     in "ParameterSettings" code. 
+%  
+%     INPUT:
+% 
+%     - proj = 2D projections for each angle 
+%     - param = Parameter of all geometry
+%     - projNumber = Vector with projections numbers to be processed
+%
+%     OUTPUT:
+% 
+%     - data3d = reconstructed volume.
+% 
+%     Reference: Three-Dimensional Digital Tomosynthesis - Yulia 
+%     Levakhina (2014), Cap 3.6 and 3.7.
+%
+%     Original Paper: De Man, Bruno, and Samit Basu. "Distance-driven 
+%     projection and backprojection in three dimensions." Physics in 
+%     Medicine & Biology (2004).
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Distance Driven Back-projection Code
+function data3d = backprojectionDD(proj,param,projNumber)
+
+% Stack of reconstructed slices
+data3d = zeros(param.ny, param.nx, param.nz,'single');
+
+
+% Map detector and object boudaries
+param.us = (param.nu:-1:0)*param.du;
+param.vs = (-(param.nv)/2:1:(param.nv)/2)*param.dv;
+param.xs = (param.nx:-1:0)*param.dx;
+param.ys = (-(param.ny)/2:1:(param.ny)/2)*param.dy;
+param.zs = (0:1:param.nz-1)*param.dz + param.DAG + (param.dz/2);
+
+% Detector boudaries coordinate sytem in (mm)
+[detX,detY] = meshgrid(param.us,param.vs);
+detZ = zeros(size(detX));
+
+% Object boudaries coordinate sytem in (mm)
+[objX,objY] = meshgrid(param.xs,param.ys);
+objZ = param.zs;     % Z object coordinates
+
+% X-ray tube initial position
+tubeX = 0;
+tubeY = 0;
+tubeZ = param.DSD;
+
+% Iso-center position
+isoY = 0;
+isoZ = param.DDR;
+
+% Projection angles
+tubeAngle = deg2rad(param.tubeDeg);
+detAngle = deg2rad(param.detectorDeg);
+
+% Number of detector and voxels for each direction
+nDetX = param.nu;
+nDetY = param.nv;
+nPixX = param.nx;
+nPixY = param.ny;
+
+nSlices = param.nz;
+nProjs = param.nProj;
+
+% Voxel size on Z
+pixsZ = param.dz; 
+
+% Test if there's specific angles
+if(isempty(projNumber))
+    projNumber = 1:nProjs;
+else
+    if(max(projNumber(:)) <= nProjs)
+        nProjs = size(projNumber,2);
+    else
+        error('Projection number exceeds the maximum for the equipment.')
+    end
+end
+
+proj = fliplr(proj);
+
+% For each projection
+for p=1:nProjs
+    
+    % Get specific projection number
+    projN = projNumber(p);
+    
+    % Get specifc projection for specif angle
+    projAngle = proj(:,:,p);
+    
+    % Get specif tube angle for the projection
+    theta = tubeAngle(projN);
+    
+    % Get specif detector angle for the projection
+    phi = detAngle(projN);
+    
+    % Tubre rotation
+    rtubeY = ((tubeY - isoY)*cos(theta)-(tubeZ - isoZ)*sin(theta) )+isoY;
+    rtubeZ = ((tubeY - isoY)*sin(theta)+(tubeZ - isoZ)*cos(theta) )+isoZ;
+
+    % Detector rotation
+    rdetY = ((detY - isoY).*cos(phi)-(detZ - isoZ).*sin(phi) )+isoY;
+    rdetZ = ((detY - isoY).*sin(phi)+(detZ - isoZ).*cos(phi) )+isoZ;
+    
+     
+    % Map detector onto XY plane(Inside proj loop in case detector rotates)
+    [detmX,detmY] = mapp2xy(tubeX,rtubeY,rtubeZ,detX,rdetY,rdetZ);
+    
+    % Z coord does not change in X direction, so we can take only one
+    % collumn of Y mapped detecotr boundaries
+    detmY = detmY(:,1);        
+   
+    % Detector start index and increment
+    detIstart = 1;
+    detIinc = 1;    
+    % Mapped detector length
+    deltaDetmY = detmY(detIstart+detIinc)-detmY(detIstart);   
+        
+    % For each slice
+    for nz=1:nSlices
+        
+        % Temporary variable for each slice
+        slice = zeros(nPixY,nPixX);
+                  
+        % Map slice onto XY plane
+        [pixmX,pixmY] = mapp2xy(tubeX,rtubeY,rtubeZ,objX,objY,objZ(nz)); 
+        
+        %  - Z coord does not change in one slice, so we can take just one 
+        %    line of mapped coordinates per slice
+        %  - Flip X (Img coord is reverse to Global)
+        pixmX = pixmX(1,end:-1:1);
+        pixmY = pixmY(:,1);       
+        
+        % Pixel start index and increment
+        pixIstart = 1;
+        pixIinc = 1; 
+        % Mapped pixel length
+        deltaPixmX = pixmX(pixIstart+pixIinc)-pixmX(pixIstart);
+        deltaPixmY = pixmY(pixIstart+pixIinc)-pixmY(pixIstart);
+        
+        % Start pixel and detector indices
+        detIndY = detIstart;
+        pixIndY = pixIstart;                 
+        
+        % Case 1
+        % Find first detector overlap maped with pixel maped on Y
+        if(detmY(detIndY)-pixmY(pixIstart)<-deltaDetmY)
+            while(detmY(detIndY)-pixmY(pixIstart)<-deltaDetmY)            
+                detIndY = detIndY + detIinc;            
+            end
+        else
+        % Case 2    
+        % Find first pixel overlap maped with detector maped on Y            
+            if(detmY(detIstart)-pixmY(pixIndY)>deltaPixmY)
+                while(detmY(detIstart)-pixmY(pixIndY)>deltaPixmY)            
+                    pixIndY = pixIndY + pixIinc;            
+                end
+            end
+        end
+        
+        % Get the left coordinate of the first overlap on Y axis
+        % Try the following lines for a better understanding
+        % % ---------------------------------------------------------------
+        % % figure
+        % % plot(detmY(:),zeros(1,size(detmY,1)),'r.','MarkerSize',6)
+        % % hold on
+        % % plot(pixmY(:),zeros(1,size(pixmY,1)),'b.','MarkerSize',6)
+        % % hold off
+        % % legend('Detector Boundaries','Pixel Boundaries')
+        % % title('Overlap on Y axis')
+        % % ---------------------------------------------------------------
+        if( detmY(detIndY) < pixmY(pixIndY) )
+            moving_left_boundaryY = pixmY(pixIndY);            
+        else
+            moving_left_boundaryY = detmY(detIndY);
+        end       
+        
+        % Loop over Y intersections
+        while((detIndY<=nDetY)&&(pixIndY<=nPixY))
+             
+            alpha = atan((detmY(detIndY)+(deltaDetmY/2)-rtubeY)/rtubeZ);
+            
+            % Case A, when you jump to the next detector boundarie but stay
+            % in the same pixel
+            if(detmY(detIndY+1)<=pixmY(pixIndY+1))
+                overLapY = (detmY(detIndY+1)- moving_left_boundaryY)...
+                    /deltaDetmY; % Normalized overlap Calculation
+            else
+            % Case B, when you jump to the next pixel boundarie but stay 
+            % in the same detector
+                overLapY = (pixmY(pixIndY+1)- moving_left_boundaryY)...
+                    /deltaDetmY; % Normalized overlap Calculation
+            end
+                                 
+            %% X overlap    
+            detIndX = detIstart;
+            pixIndX = pixIstart; 
+            
+            % Get row/coll of X, which correspond to that Y overlap det
+            detmXrow = detmX(detIndY,end:-1:1);
+            
+            % Mapped detecor length on X
+            deltaDetmX = detmXrow(detIstart+detIinc)-detmXrow(detIstart); 
+            
+            % Case 1
+            % Find first detector overlap maped with pixel maped on X 
+            if(detmXrow(detIndX)-pixmX(pixIstart)<-deltaDetmX)
+                while(detmXrow(detIndX)-pixmX(pixIstart)<-deltaDetmX)            
+                    detIndX = detIndX + detIinc;            
+                end
+            else
+            % Case 2
+            % Find first pixel overlap maped with detector maped on X            
+                if(detmXrow(detIstart)-pixmX(pixIndX)>deltaPixmX)
+                    while(detmXrow(detIstart)-pixmX(pixIndY)>deltaPixmX)            
+                        pixIndX = pixIndX + pixIinc;            
+                    end
+                end
+            end
+
+            % Get the left coordinate of the first overlap on X axis
+            % Try the following lines for a better understanding
+            % % ---------------------------------------------------------------
+            % % figure
+            % % plot(detmXrow(:),zeros(1,size(detmXrow,2)),'r.','MarkerSize',6)
+            % % hold on
+            % % plot(pixmX(:),zeros(1,size(pixmX,1)),'b.','MarkerSize',6)
+            % % hold off
+            % % legend('Detector Boundaries','Pixel Boundaries')
+            % % title('Overlap on X axis')
+            % % ---------------------------------------------------------------
+
+            if( detmXrow(detIndX) < pixmX(pixIndX) )
+                moving_left_boundaryX = pixmX(pixIndX);            
+            else
+                moving_left_boundaryX = detmXrow(detIndX);
+            end
+        
+                
+            % Loop over X intersections
+            while((detIndX<=nDetX)&&(pixIndX<=nPixX))
+                 
+                gamma = atan((detmXrow(detIndX)+(deltaDetmX/2)-tubeX)/rtubeZ);                
+                
+                % Case A, when you jump to the next detector boundarie but stay
+                % in the same pixel
+                if(detmXrow(detIndX+1)<=pixmX(pixIndX+1))
+                    
+                    overLapX = (detmXrow(detIndX+1)- moving_left_boundaryX)...
+                    /deltaDetmX; % Normalized overlap Calculation
+                                       
+                    slice((pixIndX-1)*nPixY+pixIndY) =  ...
+                    slice((pixIndX-1)*nPixY+pixIndY) ...
+                    + overLapX * overLapY * projAngle((detIndX-1)*nDetY+detIndY)...
+                    * pixsZ/(cos(alpha)*cos(gamma));               
+                
+                    detIndX = detIndX + detIinc;
+                    moving_left_boundaryX = detmXrow(detIndX);
+                    
+                else
+                % Case B, when you jump to the next pixel boundarie but stay 
+                % in the same detector
+                
+                    overLapX = (pixmX(pixIndX+1)- moving_left_boundaryX)...
+                    /deltaDetmX; % Normalized overlap Calculation
+                                            
+                    slice((pixIndX-1)*nPixY+pixIndY) = ...
+                    slice((pixIndX-1)*nPixY+pixIndY)...    
+                    + overLapX * overLapY * projAngle((detIndX-1)*nDetY+detIndY)...
+                    * pixsZ/(cos(alpha)*cos(gamma));
+                
+                    pixIndX = pixIndX + pixIinc;
+                    moving_left_boundaryX = pixmX(pixIndX);
+                    
+                end
+            end
+           
+            %% Back to Y overlap
+            
+            % Case A, when you jump to the next detector boundarie but stay
+            % in the same pixel
+            if(detmY(detIndY+1)<=pixmY(pixIndY+1))
+                detIndY = detIndY + detIinc;
+                moving_left_boundaryY = detmY(detIndY);
+            else
+            % Case B, when you jump to the next pixel boundarie but stay 
+            % in the same detector
+                pixIndY = pixIndY + pixIinc;
+                moving_left_boundaryY = pixmY(pixIndY);
+            end
+            
+        end % Y Overlap loop
+        
+        % Accumulate for each proj angle
+        data3d(:,:,nz) = data3d(:,:,nz) + fliplr(slice);
+                     
+    end % Loop end slices
+        
+end % Loop end Projections
+data3d = data3d ./ nProjs;
+end
+%% Map on XY plane
+function [x,y] = mapp2xy(x1,y1,z1,x2,y2,z2)
+    x = x2 + z2 .* (x2-x1)./(z1-z2);
+    y = y2 + z2 .* (y2-y1)./(z1-z2);
+end

+ 228 - 0
Functions/backprojectionDDb.m

@@ -0,0 +1,228 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: March, 2019
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 backprojectionDDb(proj,param,projNumber)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function reconstruct the 3D volume from projections, based on 
+%     the Branchless Distance-Driven principle. 
+%     The geometry is for DBT with half cone-beam. All parameters are set 
+%     in "ParameterSettings" code. 
+%  
+%     INPUT:
+% 
+%     - proj = 2D projections for each angle 
+%     - param = Parameter of all geometry
+%     - projNumber = Vector with projections numbers to be processed
+%
+%     OUTPUT:
+% 
+%     - data3d = reconstructed volume.
+% 
+%     Reference: 
+%     - Branchless Distance Driven Projection and Backprojection,
+%     Samit Basu and Bruno De Man (2006)
+%     - GPU Acceleration of Branchless Distance Driven Projection and 
+%     Backprojection, Liu et al (2016)
+%     - GPU-Based Branchless Distance-Driven Projection and Backprojection,
+%     Liu et al (2017)
+%     - A GPU Implementation of Distance-Driven Computed Tomography, 
+%     Ryan D. Wagner (2017)
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Backprojection Branchless Distance Driven Code
+
+function data3d = backprojectionDDb(proj,param,projNumber)
+
+% Map detector and object boudaries
+param.us = double((param.nu:-1:0)*param.du);
+param.vs = double((-(param.nv)/2:1:(param.nv)/2)*param.dv);
+param.xs = double((param.nx:-1:0)*param.dx);
+param.ys = double((-(param.ny)/2:1:(param.ny)/2)*param.dy);
+param.zs = double((0:1:param.nz-1)*param.dz + param.DAG + (param.dz/2));
+
+% Detector boudaries coordinate sytem in (mm)
+[detX,detY] = meshgrid(param.us,param.vs);
+detZ = zeros(size(detX),'double');
+
+% Object boudaries coordinate sytem in (mm)
+[objX,objY] = meshgrid(param.xs,param.ys);
+objZ = double(param.zs);     % Z object coordinates
+
+% X-ray tube initial position
+tubeX = double(0);
+tubeY = double(0);
+tubeZ = double(param.DSD);
+
+% Iso-center position
+isoY = double(0);
+isoZ = double(param.DDR);
+
+% Projection angles
+tubeAngle = double(deg2rad(param.tubeDeg));
+detAngle = double(deg2rad(param.detectorDeg));
+
+% Number of detector and voxels for each direction
+nDetX = double(param.nu);
+nDetY = double(param.nv);
+nPixX = double(param.nx);
+nPixY = double(param.ny);
+
+deltaDetX = double(param.du); 
+deltaDetY = double(param.dv); 
+deltaObjX = double(param.dx); 
+deltaObjY = double(param.dy);
+
+nSlices = double(param.nz);
+nProjs = double(param.nProj);
+
+% Voxel size on Z
+pixsZ = double(param.dz); 
+
+% Test if there's specific angles
+if(isempty(projNumber))
+    projNumber = 1:nProjs;
+else
+    if(max(projNumber(:)) <= nProjs)
+        nProjs = size(projNumber,2);
+    else
+        error('Projection number exceeds the maximum for the equipment.')
+    end
+end
+
+% Stack of reconstructed slices
+data3d = zeros(param.ny, param.nx, param.nz,'double');
+
+% Integration of each 2D projection
+% (S.1. Integration - Liu et al (2017))
+projI = integrate2D(proj);
+
+% For each projection
+for p=1:nProjs
+       
+    % Get specific projection number
+    projN = projNumber(p);
+    
+    % Get specif tube angle for the projection
+    theta = tubeAngle(projN);
+    
+    % Get specif detector angle for the projection
+    phi = detAngle(projN);
+    
+    % Tubre rotation
+    rtubeY = ((tubeY - isoY)*cos(theta)-(tubeZ - isoZ)*sin(theta) )+isoY;
+    rtubeZ = ((tubeY - isoY)*sin(theta)+(tubeZ - isoZ)*cos(theta) )+isoZ;
+
+    % Detector rotation
+    rdetY = ((detY - isoY).*cos(phi)-(detZ - isoZ).*sin(phi) )+isoY;
+    rdetZ = ((detY - isoY).*sin(phi)+(detZ - isoZ).*cos(phi) )+isoZ;
+    
+    
+    % For each slice
+    for nz=1:nSlices
+        
+        % Temporary variable for each slice
+        slice = zeros(nPixY,nPixX,'double');
+                  
+        % Map detector onto XY plane in the specifc slice   
+        [detmX,detmY] = mapp2xyZ(tubeX,rtubeY,rtubeZ,detX,rdetY,rdetZ,objZ(nz));
+        
+        % Bilinear interpolation of the integrated proj(proj coords) on
+        % image slice coords
+        % (S.2. Interpolation - Liu et al (2017))
+        sliceI = interp2(detmX,detmY,projI(:,:,p),objX,objY,'linear',0);
+                
+        for xPix=2:nPixX+1
+    
+            % x-ray angle in X coord
+            gamma = atan((objX(1,xPix-1)+(deltaObjX/2)-tubeX)/(rtubeZ - objZ(nz))); 
+            
+            for yPix=2:nPixY+1                            
+                
+                % x-ray angle in Y coord
+                alpha = atan((objY(yPix-1,1)+(deltaObjY/2)-rtubeY)/(rtubeZ - objZ(nz)));              
+                
+                % S.3. Differentiation - Eq. 24 - Liu et al (2017)
+                slice(yPix-1,xPix-1) = slice(yPix-1,xPix-1)...
+                                         +((sliceI(yPix,xPix)...
+                                         -sliceI(yPix,xPix-1)...
+                                         -sliceI(yPix-1,xPix)...
+                                         +sliceI(yPix-1,xPix-1))...
+                                         *(deltaDetX.*deltaDetY.*pixsZ./(cos(alpha)*cos(gamma)))...
+                                         *(1/(deltaObjX*deltaObjY)));
+                                                                              
+            end % Loop end Y coord
+        end % Loop end X coord
+      
+        % Accumulate for each proj angle
+        data3d(:,:,nz) = data3d(:,:,nz) + slice;
+        
+    end % Loop end slices     
+end % Loop end Projections
+
+data3d = data3d ./ nProjs;
+
+end
+
+%% Integrate function
+function [Ppj] = integrate2D (imageStack)
+
+%         2-D image
+%
+%     | -> J
+%     v -------------
+%     I |           |
+%       |           |
+%       |           |
+%       |           |
+%       -------------
+
+ni_pixel = size(imageStack,1)+1;
+nj_pixel = size(imageStack,2)+1;
+n_stack = size(imageStack,3);
+
+p_v = zeros(ni_pixel,nj_pixel,n_stack,'double');    % Pad with zeros
+p_v(2:end,2:end,:) = imageStack;            % Load slices
+
+P_i = zeros(ni_pixel,1,n_stack,'double');
+P_j = zeros(1,nj_pixel,n_stack,'double');
+
+Ppj = zeros(ni_pixel,nj_pixel,n_stack,'double');
+
+% Integrate on J direction
+for pj=2:nj_pixel    
+   P_i = P_i + p_v(:,pj,:);
+   Ppj(:,pj,:) = P_i;
+end
+
+% Integrate on I direction
+for pi=2:ni_pixel    
+   P_j = P_j + Ppj(pi,:,:);
+   Ppj(pi,:,:) = P_j;
+end
+
+end
+
+%% Map on XY plane (on specific slice)
+function [x,y] = mapp2xyZ(x1,y1,z1,x2,y2,z2,z)
+    x = ((x2-x1).*(z-z2)-(x2.*z1)+(x2.*z2))./(-z1+z2);
+    y = ((y2-y1).*(z-z2)-(y2.*z1)+(y2.*z2))./(-z1+z2);
+end

+ 72 - 0
Functions/dataPreprocess.m

@@ -0,0 +1,72 @@
+%% Author: Rodrigo de Barros Vimieiro / Aleks Prša
+% Date: April, 2018 / May 2022
+% rodrigo.vimieiro@gmail.com / alex.prsa@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                    dataPreprocess(data,parameter)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function pre process the projection images.
+%  
+%  
+%     INPUT:
+% 
+%     - data = Projections.
+%     - parameter = Parameters.
+% 
+%     OUTPUT:
+% 
+%     - data_mod = Modified image.
+%     - parameter_mod = Modified parameters.
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%
+%}
+% =========================================================================
+%% Pre-processing Code
+
+function [data_mod,parameter_mod] = dataPreprocess(data,parameter) 
+
+parameter_mod = parameter;
+
+Gap = 200;
+meanSlideW = 201;
+
+centerProj = (parameter.nProj+1)/2;                      % Center projection - 90° projection
+
+if(rem(centerProj,2) == 0)
+    fprintf('Center projection determination error')
+    return
+end
+
+vertProj = sum(abs(data(:,:,centerProj)));	            % Horizontal Profile
+[~,Ind] = max(diff(movmean(vertProj,meanSlideW)));		% Smooth the signal and takes its max positive derivative
+Ind = Ind + Gap;                                        % Subtract the ind found to a gap
+data_mod = data(:,1:Ind,:);
+
+%data_mod = flip(data_mod, 2);                          % Switch source position left-to-right
+
+% Modifies parameters based on segmentation
+parameter_mod.nu = size(data_mod,2);                    % Number of pixels (columns)
+parameter_mod.nv = size(data_mod,1);                    % Number of pixels (rows) 
+parameter_mod.su = parameter_mod.nu.*parameter_mod.du;	
+parameter_mod.sv = parameter_mod.nv.*parameter_mod.dv; 
+parameter_mod.us = (parameter_mod.nu-1:-1:0)*parameter_mod.du;
+parameter_mod.vs = (-(parameter_mod.nv-1)/2:1:(parameter_mod.nv-1)/2)*parameter_mod.dv;
+
+end

+ 109 - 0
Functions/draw3d.m

@@ -0,0 +1,109 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: April, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% 
+%     DESCRIPTION:
+%     Draw x-ray source, axis, projection plane and object plane.
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%
+%}
+% =========================================================================
+%% Draw 3D animation
+function draw3d(xCoord,yCoord,zCoord,puCoord,pvCoord,param,projnum,theta)
+
+sb = subplot(2,2,[1 3]);    % Create a subplot and merge 1 and 3
+cla(sb)                     % Clear current subplot window 
+
+% Detector Coordinate sytem in (mm)
+[uCoord,vCoord] = meshgrid(param.us,param.vs);
+
+axis([param.us(1,end),param.us(1,1)+10,param.vs(1,1)-10,param.vs(1,end)+10,-5,1.1*param.DSD]);
+xlabel(['x(mm)'],'FontSize', 20);% / ', num2str(param.nx),'(Voxel Unity)'])
+ylabel(['y(mm)'],'FontSize', 20);% / ', num2str(param.ny),'(Voxel Unity)'])
+zlabel(['z(mm)'],'FontSize', 20);%;% / ', num2str(param.nz),'(Voxel Unity)'])
+title(num2str(projnum))
+view([94.6,27.4])
+grid on
+hold on
+
+% Detector plane vertices coordinates
+vetU = [uCoord(1,1),uCoord(end,1),uCoord(end,end),uCoord(1,end)];
+vetV = [vCoord(1,1),vCoord(end,1),vCoord(end,end),vCoord(1,end)];
+
+% Projection plane vertices coordinates
+vetPu = [puCoord(1,1),puCoord(end,1),puCoord(end,end),puCoord(1,end)];
+vetPv = [pvCoord(1,1),pvCoord(end,1),pvCoord(end,end),pvCoord(1,end)];
+
+% Object volume vertices coordinates
+vetObj =  [ xCoord(1,1)     yCoord(1,1)       zCoord(1,1)
+            xCoord(end,1)   yCoord(end,1)     zCoord(1,1)
+            xCoord(end,end) yCoord(end,end)   zCoord(1,1)
+            xCoord(1,end)   yCoord(1,end)     zCoord(1,1)
+            xCoord(1,1)     yCoord(1,1)       zCoord(end,1)
+            xCoord(end,1)   yCoord(end,1)     zCoord(end,1)
+            xCoord(end,end) yCoord(end,end)   zCoord(end,1)
+            xCoord(1,end)   yCoord(1,end)     zCoord(end,1)];
+
+% Link of vertices for each face
+vetor_faces =  [1 2 3 4
+                5 6 7 8
+                1 2 6 5
+                3 4 8 7
+                1 4 8 5
+                2 6 7 3];
+            
+%% Draw Everything
+
+% Draw the world's coordinate system to use as reference
+%   - Colors: x in red, y in green, z in blue.
+%drawAxes([0,0,0,0,0,0], [0.15*param.su, 0.15*param.su, 0.1*param.sz], 2, ['r','g','b']);
+
+% Draw the sensor coordinate system to use as reference
+%   - Colors: x in red, y in green, z in blue.
+%drawAxes([param.us(1),param.vs(1),0,0,0,0], [-0.15*param.su, 0.15*param.su, 0], 2, ['r','g','b']);
+
+% Rotate x-ray source
+xSource = 0;
+ySource = double(param.DSR.*sin(-theta));
+zSource = double(param.DSR.*cos(theta)+param.DDR);
+% Draw x-ray source
+plot3(xSource,ySource,zSource,'r.','markersize',45)
+% Draw text
+text(xSource,ySource-.15*abs(ySource),1.04*zSource,'X-Ray Source','FontSize', 16)
+
+% Draw Detector plane
+patch(vetU,vetV,[0,0,0,0],'FaceColor','none','EdgeColor','black','linewidth',2,'LineStyle','--')
+
+% Draw Projection plane
+patch(vetPu,vetPv,[0,0,0,0],'yellow','linewidth',1)
+
+% Draw Object volume
+patch('Vertices',vetObj,'Faces',vetor_faces,'FaceColor', 'blue','FaceAlpha',0.6)
+
+% Draw lines from the X-ray source to the projection plane passing 
+% through the upper slice of the object
+plot3([xSource,xCoord(1,1),puCoord(1,1)],[ySource,yCoord(1,1),pvCoord(1,1)],[zSource,zCoord(end,1),0],'--')
+plot3([xSource,xCoord(end,1),puCoord(end,1)],[ySource,yCoord(end,1),pvCoord(end,1)],[zSource,zCoord(end,1),0],'--')
+plot3([xSource,xCoord(1,end),puCoord(1,end)],[ySource,yCoord(1,end),pvCoord(1,end)],[zSource,zCoord(end,1),0],'--')
+plot3([xSource,xCoord(end,end),puCoord(end,end)],[ySource,yCoord(end,end),pvCoord(end,end)],[zSource,zCoord(end,1),0],'--')
+
+drawnow
+
+end

+ 38 - 0
Functions/figureScreenSize.m

@@ -0,0 +1,38 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: April, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% 
+%     DESCRIPTION:
+%     Create a figure of 80% of sreen size.
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%
+%}
+% =========================================================================
+%% Create a figure
+
+function  figureScreenSize()
+screen_size = get(0,'Screensize');
+w = screen_size(3);
+h = screen_size(4);
+fw = round(w*0.8);
+fh = round(h*0.8);
+fig_opt = [(w-fw)/2 (h-fh)/2 fw fh];
+figure("Name", 'Animation', NumberTitle='off', Position=fig_opt)
+end

+ 144 - 0
Functions/filterProj.m

@@ -0,0 +1,144 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: April, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 filterProj(proj,param,cutoff,filterOrder)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function filters the projection based on Feldkamp algorithms. It
+%     also applies a window filter to discourage noise and avoid ringing
+%     effect.
+% 
+%     The geometry is for DBT with half cone-beam. All parameters are set 
+%      in "ParameterSettings" code. 
+%  
+%     INPUT:
+% 
+%     - proj = 2D projection images 
+%     - param = Parameter of all geometry
+%     - cutoff = Cut off frequency up to Nyquist
+%     - filterOrder = Butterworth filter order
+% 
+%     OUTPUT:
+% 
+%     - filteredProj = Filtered projections.
+% 
+%     Reference: Jiang Hsieh's book (second edition)
+%     Reference: Fessler, J. A.: Fundamentals of CT Reconstruction in 2D 
+%     and 3D. In: Brahme, A. (eds.) Comprehensive Biomedical Physics, 
+%     1st ed., vol. 2, pp 263-295. Elsevier, Netherlands (2014).
+%     doi:10.1016/B978-0-444-53632-7.00212-4.
+%     Reference: Fessler Book -> (http://web.eecs.umich.edu/~fessler/book/c-tomo.pdf)
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% Filtering Code
+function filteredProj = filterProj(proj,param,cutoff,filterOrder)
+
+filteredProj = zeros(size(proj),'single');
+
+% Detector Coordinate sytem in (mm)
+[uCoord,vCoord] = meshgrid(param.us,param.vs);
+
+% Compute weighted projections (Fessler Book Eq. (3.10.6))
+weightFunction = param.DSO ./ sqrt((param.DSD.^2)+(vCoord.^2)+(uCoord.^2));
+                   
+% Apply weighting function on each proj
+for i=1:param.nProj
+    filteredProj(:,:,i) = proj(:,:,i).* weightFunction;
+end
+
+% Increase filter length to two times nv to decrease freq step
+h_Length = 2^nextpow2(2*param.nv);
+
+% Builds ramp filter in space domain
+ramp_kernel = ramp_builder(h_Length);
+
+% Window filter in freq domain
+H_filter = filter_window(ramp_kernel, h_Length, cutoff, filterOrder);
+
+% Replicate to all columns to build a 2D filter kernel
+H_filter = repmat(H_filter',1,param.nu);
+
+% Proj in freq domain
+H_proj = zeros(h_Length,param.nu,'single');
+
+fprintf('%2d/%2d', 1, param.nProj)
+
+% Filter each projection
+for i=1:param.nProj
+
+   fprintf('\b\b\b\b\b%2d/%2d', i, param.nProj)
+
+   H_proj(1:param.nv,:) = filteredProj(:,:,i);
+
+   % Fourier transfor in projections
+   H_proj = fftshift(fft(H_proj));
+       
+   % Multiplication in frequency = convolution in space
+   H_proj = H_proj .* H_filter;
+    
+   % Inverse Fourier transfor
+   H_proj = (real(ifft(ifftshift(H_proj))));
+
+   filteredProj(:,:,i) = H_proj(1:param.nv,:);
+
+end
+
+end
+
+%% Function Ramp Filter
+%{
+The function builds Ramp Filter in space domain
+Reference: Jiang Hsieh's book (second edition,page 73) Eq. 3.29
+Reference: Fessler Book Eq.(3.4.14)
+%}
+function h = ramp_builder(h_Length)
+
+n = (-(h_Length/2):(h_Length/2-1));
+h = zeros(size(n),'single');
+h(h_Length/2+1) = 1/4;  % Eq. 3.29
+odd = mod(n,2) == 1;    % Eq. 3.29
+h(odd) = -1 ./ (pi * n(odd)).^2; % Eq. 3.29
+
+end
+
+%% Function Window Ramp Filter
+%{
+The function builds Ramp Filter apodizided in frequency domain
+Reference: Fessler Book and MIRT
+%}
+function H_filter = filter_window(ramp_kernel, h_Length, cutoff, filterOrder)
+
+H_ramp = abs(fftshift(fft(ramp_kernel))); % Bring filter to freq domain
+
+w = round(h_Length*cutoff); % Cut off freq
+n = (-(h_Length/2):(h_Length/2-1));
+
+
+% Hanning filter + applying cut off frequency
+H_window = 0.5 * (1 + cos(2*pi*n/w)); % Normal Hanning
+H_window = H_window .* (abs(n) < w/2); % Apply cut off freq
+
+%H_window = 1 ./ (1 + (n/w).^ (2*filterOrder)); %Butterworth
+
+H_filter = H_ramp .* H_window; % Apply window filter
+
+end

+ 63 - 0
Functions/phantom.m

@@ -0,0 +1,63 @@
+%% Author: Aleks Prša
+% Date: July, 2022
+% alex.prsa@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                           phantom(parameter)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function create a 3D virtual phantom.
+%  
+%     INPUT:
+%     - parameter = Parameter of all geometry
+% 
+%     OUTPUT:
+%     - phantom = Virtual phantom attenuation data.
+% 
+%     ---------------------------------------------------------------------
+%}
+% =========================================================================
+%% Create virtual phantom
+function data3d = phantom(parameter)
+
+data3d = zeros(parameter.nx, parameter.ny, parameter.nz);
+
+xyzr = readmatrix('Mikrokalcinacije.txt');
+
+for i = 1:size(xyzr, 1)
+    mu = xyzr(i, 1);                    % Attenuation coefficient of microcalcification (1/mm)
+    y0 = xyzr(i, 2);                    % y offset of microcalcification in pixels
+    x0 = xyzr(i, 3);                    % x offset of microcalcification in pixels
+    z0 = xyzr(i, 4);                    % z offset of microcalcification in slice number of original tomogram
+    r = xyzr(i, 5) / 2000;              % Radius of microcalcification (mm)
+
+    z0 = (z0-1) * 10;                   % Transform z offset from slice number to pixel value
+
+    a = r / parameter.dx;               % Radius in x-axis
+    b = r / parameter.dy;               % Radius in y-axis
+    c = r / parameter.dz;               % Radius in z-axis
+
+    a = a^2;
+    b = b^2;
+    c = c^2;
+
+    for z = 1:parameter.nz
+        for y = 1:parameter.ny
+            for x = 1:parameter.nx
+                if((x-x0)^2 / a + (y-y0)^2 / b + (z-z0)^2 / c <= 1) 
+                    data3d(x,y,z) = data3d(x,y,z) + mu;
+                end
+            end
+        end
+    end
+end
+
+data3d = data3d - 0.03756;                              % Attenuation correction - microcalcification-H2O
+data3d(data3d < 0) = 0;
+
+%data3d(data3d == 0) = 0.0357776;                       % Attenuation coefficient of PMMA (1/mm) surrounding microcalcifications
+
+data3d = flip(data3d, 1);
+
+end

+ 234 - 0
Functions/phantom3d.m

@@ -0,0 +1,234 @@
+% PHANTOM3D Three-dimensional analogue of MATLAB Shepp-Logan phantom
+%   P = PHANTOM3D(DEF,N) generates a 3D head phantom that can   
+%   be used to test 3-D reconstruction algorithms.
+%
+%   DEF is a string that specifies the type of head phantom to generate.
+%   Valid values are: 
+%         
+%      'Shepp-Logan'            A test image used widely by researchers in
+%                               tomography
+%      'Modified Shepp-Logan'   (default) A variant of the Shepp-Logan phantom
+%                               in which the contrast is improved for better  
+%                               visual perception.
+%
+%   N is a scalar that specifies the grid size of P.
+%   If you omit the argument, N defaults to 64.
+% 
+%   P = PHANTOM3D(E,N) generates a user-defined phantom, where each row
+%   of the matrix E specifies an ellipsoid in the image.  E has ten columns,
+%   with each column containing a different parameter for the ellipsoids:
+%   
+%     Column 1:  A      the additive intensity value of the ellipsoid
+%     Column 2:  a      the length of the x semi-axis of the ellipsoid 
+%     Column 3:  b      the length of the y semi-axis of the ellipsoid
+%     Column 4:  c      the length of the z semi-axis of the ellipsoid
+%     Column 5:  x0     the x-coordinate of the center of the ellipsoid
+%     Column 6:  y0     the y-coordinate of the center of the ellipsoid
+%     Column 7:  z0     the z-coordinate of the center of the ellipsoid
+%     Column 8:  phi    phi Euler angle (in degrees) (rotation about z-axis)
+%     Column 9:  theta  theta Euler angle (in degrees) (rotation about x-axis)
+%     Column 10: psi    psi Euler angle (in degrees) (rotation about z-axis)
+%
+%   For purposes of generating the phantom, the domains for the x-, y-, and 
+%   z-axes span [-1,1].  Columns 2 through 7 must be specified in terms
+%   of this range.
+%
+%   [P,E] = PHANTOM3D(...) returns the matrix E used to generate the phantom.
+%
+%   Class Support
+%   -------------
+%   All inputs must be of class double.  All outputs are of class double.
+%
+%   Remarks
+%   -------
+%   For any given voxel in the output image, the voxel's value is equal to the
+%   sum of the additive intensity values of all ellipsoids that the voxel is a 
+%   part of.  If a voxel is not part of any ellipsoid, its value is 0.  
+%
+%   The additive intensity value A for an ellipsoid can be positive or negative;
+%   if it is negative, the ellipsoid will be darker than the surrounding pixels.
+%   Note that, depending on the values of A, some voxels may have values outside
+%   the range [0,1].
+%    
+%   Example
+%   -------
+%        ph = phantom3d(128);
+%        figure, imshow(squeeze(ph(64,:,:)))
+%
+%   Copyright 2005 Matthias Christian Schabel (matthias @ stanfordalumni . org)
+%   University of Utah Department of Radiology
+%   Utah Center for Advanced Imaging Research
+%   729 Arapeen Drive
+%   Salt Lake City, UT 84108-1218
+%   
+%   This code is released under the Gnu Public License (GPL). For more information, 
+%   see : http://www.gnu.org/copyleft/gpl.html
+%
+%   Portions of this code are based on phantom.m, copyrighted by the Mathworks
+%
+% =========================================================================
+%% Phantom Code
+
+function [p,ellipse]=phantom3d(varargin)
+
+[ellipse,n] = parse_inputs(varargin{:});
+
+p = ones([n n n]) .* 0.0357776;           % Attenuation coefficient of PMMA (1/mm)
+%p = zeros([n n n]);
+
+rng =  ((0:n-1)-(n-1)/2) / ((n-1)/2); 
+
+[x,y,z] = meshgrid(rng,rng,rng);          % Coordinate system of a phantom; starting point at the phantom's centre
+
+coord = [flatten(x); flatten(y); flatten(z)];
+
+p = flatten(p);
+
+for k = 1:size(ellipse,1)    
+   A = ellipse(k,1);            % Amplitude change for this ellipsoid
+   asq = ellipse(k,2)^2;        % a^2
+   bsq = ellipse(k,3)^2;        % b^2
+   csq = ellipse(k,4)^2;        % c^2
+   x0 = ellipse(k,5);           % x offset
+   y0 = ellipse(k,6);           % y offset
+   z0 = ellipse(k,7);           % z offset
+   phi = ellipse(k,8)*pi/180;   % first Euler angle in radians
+   theta = ellipse(k,9)*pi/180; % second Euler angle in radians
+   psi = ellipse(k,10)*pi/180;  % third Euler angle in radians
+   
+   cphi = cos(phi);
+   sphi = sin(phi);
+   ctheta = cos(theta);
+   stheta = sin(theta);
+   cpsi = cos(psi);
+   spsi = sin(psi);
+   
+   % Euler rotation matrix
+   alpha = [cpsi*cphi-ctheta*sphi*spsi   cpsi*sphi+ctheta*cphi*spsi  spsi*stheta;
+            -spsi*cphi-ctheta*sphi*cpsi  -spsi*sphi+ctheta*cphi*cpsi cpsi*stheta;
+            stheta*sphi                  -stheta*cphi                ctheta];        
+   
+   % Rotated ellipsoid coordinates
+   coordp = alpha*coord;
+   
+   idx = find((coordp(1,:)-x0).^2./asq + (coordp(2,:)-y0).^2./bsq + (coordp(3,:)-z0).^2./csq <= 1);
+   p(idx) = p(idx) + A;
+end
+
+p = reshape(p,[n n n]);
+
+return;
+
+function out = flatten(in)
+
+out = reshape(in,[1 prod(size(in))]);
+
+return;
+
+function [e,n] = parse_inputs(varargin)
+%  e is the m-by-10 array which defines ellipsoids
+%  n is the size of the phantom brain image
+
+n = 128;     % The default size
+e = [];
+defaults = {'shepp-logan', 'modified shepp-logan', 'yu-ye-wang', 'aleks'};
+
+for i=1:nargin
+   if ischar(varargin{i})         % Look for a default phantom
+      def = lower(varargin{i});
+      idx = strmatch(def, defaults);
+
+      if isempty(idx)
+         eid = sprintf('Images:%s:unknownPhantom',mfilename);
+         msg = 'Unknown default phantom selected.';
+         error(eid,'%s',msg);
+      end
+
+      switch defaults{idx}
+      case 'shepp-logan'
+         e = shepp_logan;
+      case 'modified shepp-logan'
+         e = modified_shepp_logan;
+      case 'yu-ye-wang'
+         e = yu_ye_wang;
+      case 'aleks'
+          e = aleks;
+      end
+
+   elseif numel(varargin{i})==1 
+      n = varargin{i};            % a scalar is the image size
+   
+   elseif ndims(varargin{i})==2 && size(varargin{i},2)==10 
+      e = varargin{i};            % user specified phantom
+   
+   else
+      eid = sprintf('Images:%s:invalidInputArgs',mfilename);
+      msg = 'Invalid input arguments.';
+      error(eid,'%s',msg);
+   end
+end
+
+% ellipse is not yet defined
+if isempty(e)                    
+   e = modified_shepp_logan;
+end
+
+return;
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%  Default head phantoms:   %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+function e = shepp_logan
+
+e = modified_shepp_logan;
+e(:,1) = [1 -.98 -.02 -.02 .01 .01 .01 .01 .01 .01];
+
+return;
+
+function e = aleks
+
+e = readmatrix("Mikrokalcinacije.txt")
+
+return;
+      
+function e = modified_shepp_logan
+%
+%   This head phantom is the same as the Shepp-Logan except 
+%   the intensities are changed to yield higher contrast in
+%   the image.  Taken from Toft, 199-200.
+%      
+%         A      a     b     c     x0      y0      z0    phi  theta    psi
+%        -----------------------------------------------------------------
+e =    [  1  .6900  .920  .810      0       0       0      0      0      0
+        -.8  .6624  .874  .780      0  -.0184       0      0      0      0
+        -.2  .1100  .310  .220    .22       0       0    -18      0     10
+        -.2  .1600  .410  .280   -.22       0       0     18      0     10
+         .1  .2100  .250  .410      0     .35    -.15      0      0      0
+         .1  .0460  .046  .050      0      .1     .25      0      0      0
+         .1  .0460  .046  .050      0     -.1     .25      0      0      0
+         .1  .0460  .023  .050   -.08   -.605       0      0      0      0
+         .1  .0230  .023  .020      0   -.606       0      0      0      0
+         .1  .0230  .046  .020    .06   -.605       0      0      0      0 ];
+       
+return;
+          
+function e = yu_ye_wang
+%
+%   Yu H, Ye Y, Wang G, Katsevich-Type Algorithms for Variable Radius Spiral Cone-Beam CT
+%      
+%         A      a     b     c     x0      y0      z0    phi  theta    psi
+%        -----------------------------------------------------------------
+e =    [  1  .6900  .920  .900      0       0       0      0      0      0
+        -.8  .6624  .874  .880      0       0       0      0      0      0
+        -.2  .4100  .160  .210   -.22       0    -.25    108      0      0
+        -.2  .3100  .110  .220    .22       0    -.25     72      0      0
+         .2  .2100  .250  .500      0     .35    -.25      0      0      0
+         .2  .0460  .046  .046      0      .1    -.25      0      0      0
+         .1  .0460  .023  .020   -.08    -.65    -.25      0      0      0
+         .1  .0460  .023  .020    .06    -.65    -.25     90      0      0
+         .2  .0560  .040  .100    .06   -.105    .625     90      0      0
+        -.2  .0560  .056  .100      0    .100    .625      0      0      0 ];
+       
+return;

+ 34 - 0
Functions/projImage.m

@@ -0,0 +1,34 @@
+%% Author: Aleks Prša
+% Date: January, 2022
+% alex.prsa@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                           projImage(proj_tmp, parameter)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%
+%     This function converts attenuation images to signal images and adds 
+%     Poisson and Gaussian noise to projection images.
+%  
+%     INPUT:
+% 
+%     - proj_tmp = projection 
+%     - parameter = parameter of all geometry
+%
+%     OUTPUT:
+% 
+%     - dataProj = noised projection images.
+%     
+%}
+% =========================================================================
+%% Convert attenuation coefficients to signal image and add Poisson and Gaussian noise
+
+function dataProjNoise = projImage(proj_tmp, parameter)
+
+lambda = parameter.Io * exp(- parameter.dz * proj_tmp);
+I = parameter.eo .* poissrnd(lambda, parameter.nv, parameter.nu);
+D = normrnd(parameter.mu, parameter.sigma, parameter.nv, parameter.nu);
+dataProjNoise = I + D;
+
+end

+ 150 - 0
Functions/projection.m

@@ -0,0 +1,150 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: April, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 projection(data3d,param,projNumber)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function calculates for each detector pixel, which voxel is
+%     associated with that pixel in the specific projection. That is done 
+%     for all angles specified.
+%     The geometry is for DBT with half cone-beam. All parameters are set 
+%     in "ParameterSettings" code. 
+%  
+%     INPUT:
+% 
+%     - data3d = 3D volume for projection 
+%     - param = Parameter of all geometry
+%     - projNumber = Vector with projections numbers to be processed
+%
+%     OUTPUT:
+% 
+%     - proj = projection for each angle.
+% 
+%     Reference: Patent US5872828
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% Projection Code
+function proj = projection(data3d,param,projNumber)
+
+global animation
+
+% Get parameters from struct
+DSR = param.DSR;
+DDR = param.DDR;
+tubeAngle = deg2rad(param.tubeDeg);
+numVPixels = param.nv;
+numUPixels = param.nu;
+numYVoxels = param.ny;
+numXVoxels = param.nx;
+numSlices = param.nz;
+numProjs = param.nProj;
+zCoords = param.zs;             % Z object coordinates
+
+% Test if there's specific angles
+if(isempty(projNumber))
+    projNumber = 1:numProjs;
+else
+    if(max(projNumber(:)) <= numProjs)
+        numProjs = size(projNumber,2);
+    else
+        error('Projection number exceeds the maximum for the equipment.')
+    end
+end
+
+% Stack of projections
+proj = zeros(param.nv, param.nu, numProjs, 'single');
+
+% Detector Coordinate system in (mm)
+[uCoord,vCoord] = meshgrid(param.us,param.vs);
+
+% Object Coordinate system in (mm) (just for 3D visualization)
+[xCoord,yCoord] = meshgrid(param.xs,param.ys);
+[   ~  ,zCoord] = meshgrid(param.ys,param.zs);
+
+fprintf('%2d/%2d', 1, numProjs)
+
+% For each projection
+for p=1:numProjs
+
+    fprintf('\b\b\b\b\b%2d/%2d', p, numProjs)
+
+    % Get specific projection number
+    projN = projNumber(p);
+    
+    % Get specific tube angle for the projection
+    teta = tubeAngle(projN);
+    
+    % Temporary projection variable to acumulate all projection from the slices
+    proj_tmp = zeros(numVPixels, numUPixels, 'single');
+    
+    % For each slice
+    for nz=1:numSlices
+          
+        % Calculates a relation of detector and voxel coordinates
+        pyCoord = ((vCoord.*((DSR.*cos(teta))+DDR-zCoords(nz)))-(zCoords(nz).*DSR.*sin(teta)))./...
+                                            (DSR.*cos(teta)+DDR);
+        
+        pxCoord = (uCoord.*((DSR.*cos(teta))+DDR-zCoords(nz)))./...
+                             ((DSR.*cos(teta))+DDR);          
+        
+        % 3D Visualization
+        if(nz==numSlices && animation == 1)
+            figure(1)
+            % Calculus of projection on the detector for visualization
+            pvCoord = yCoord+((zCoords(nz).*((DSR.*sin(teta))+ yCoord))./...
+                                 ((DSR.*cos(teta))-zCoords(nz)));
+
+            puCoord = (xCoord.*DSR.*cos(teta))./ ...
+                   ((DSR.*cos(teta))-zCoords(nz));
+               
+            % Draw 3D animation   
+            draw3d(xCoord,yCoord,zCoord,puCoord,pvCoord,param,projN,teta);
+        end              
+             
+        % Coordinate in pixel of slice plane axis origin
+        x0 = numXVoxels;
+        y0 = numYVoxels/2;
+        
+        % Represent slice plane axis (X,Y) in the image plane axis (i,j) (covert mm to Pixels)
+        pxCoord = -(pxCoord./param.dx) + x0;
+        pyCoord =  (pyCoord./param.dy) + y0;  
+      
+        % Interpolation of the pixel coordinates of the "Slice" at the calculated pixel coordinates for the detector
+        proj_tmp = proj_tmp + interp2(data3d(:,:,nz),pxCoord,pyCoord,'linear',0);
+              
+    end % Loop end slices
+    
+    % Converting attenuation coefficients to signal image + adding noise
+    proj(:,:,p) = projImage(proj_tmp, param);
+
+    if(animation == 1)
+        % 2D Visualization
+        figure(1)
+        subplot(2,2,2)
+        imshow(imrotate(data3d(:,:,round(numSlices/2)),90),[])
+        title('Slice');axis on;
+        subplot(2,2,4)
+        imshow(imrotate(proj(:,:,p),90),[]); title(['Projection ',num2str(projN)]);axis on;
+    end 
+    
+end % Loop end Projections
+end

+ 329 - 0
Functions/projectionDD.m

@@ -0,0 +1,329 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: July, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 projectionDD(data3d,param,projNumber)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function calculates the volume projection based on the
+%     Distance-Driven principle. It works by calculating the overlap in X 
+%     and Y axis of the volume and the detector boundaries.  
+%     The geometry is for DBT with half cone-beam. All parameters are set 
+%     in "ParameterSettings" code. 
+%  
+%     INPUT:
+% 
+%     - data3d = 3D volume for projection 
+%     - param = Parameter of all geometry
+%     - projNumber = Vector with projections numbers to be processed
+%
+%     OUTPUT:
+% 
+%     - proj = projections for each angle.
+% 
+%     Reference: Three-Dimensional Digital Tomosynthesis - Yulia 
+%     Levakhina (2014), Cap 3.6 and 3.7.
+%
+%     Original Paper: De Man, Bruno, and Samit Basu. "Distance-driven 
+%     projection and backprojection in three dimensions." Physics in 
+%     Medicine & Biology (2004).
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Distance Driven Projection Code
+function proj = projectionDD(data3d,param,projNumber)
+
+
+% Map detector and object boudaries
+param.us = (param.nu:-1:0)*param.du;
+param.vs = (-(param.nv)/2:1:(param.nv)/2)*param.dv;
+param.xs = (param.nx:-1:0)*param.dx;
+param.ys = (-(param.ny)/2:1:(param.ny)/2)*param.dy;
+param.zs = (0:1:param.nz-1)*param.dz + param.DAG + (param.dz/2);
+
+% Detector boudaries coordinate sytem in (mm)
+[detX,detY] = meshgrid(param.us,param.vs);
+detZ = zeros(size(detX));
+
+% Object boudaries coordinate sytem in (mm)
+[objX,objY] = meshgrid(param.xs,param.ys);
+objZ = param.zs;     % Z object coordinates
+
+% X-ray tube initial position
+tubeX = 0;
+tubeY = 0;
+tubeZ = param.DSD;
+
+% Iso-center position
+isoY = 0;
+isoZ = param.DDR;
+
+% Projection angles
+tubeAngle = deg2rad(param.tubeDeg);
+detAngle = deg2rad(param.detectorDeg);
+
+% Number of detector and voxels for each direction
+nDetX = param.nu;
+nDetY = param.nv;
+nPixX = param.nx;
+nPixY = param.ny;
+
+nSlices = param.nz;
+nProjs = param.nProj;
+
+% Voxel size on Z
+pixsZ = param.dz; 
+
+% Test if there's specific angles
+if(isempty(projNumber))
+    projNumber = 1:nProjs;
+else
+    if(max(projNumber(:)) <= nProjs)
+        nProjs = size(projNumber,2);
+    else
+        error('Projection number exceeds the maximum for the equipment.')
+    end
+end
+
+% Stack of projections
+proj = zeros(param.nv, param.nu, nProjs,'single');
+
+% For each projection
+for p=1:nProjs
+    
+    % Temporary variable to acumulate all projection from the slices
+    proj_tmp = zeros(nDetY,nDetX,'single');
+    
+    % Get specific projection number
+    projN = projNumber(p);
+    
+    % Get specif tube angle for the projection
+    theta = tubeAngle(projN);
+    
+    % Get specif detector angle for the projection
+    phi = detAngle(projN);
+    
+    % Tubre rotation
+    rtubeY = ((tubeY - isoY)*cos(theta)-(tubeZ - isoZ)*sin(theta) )+isoY;
+    rtubeZ = ((tubeY - isoY)*sin(theta)+(tubeZ - isoZ)*cos(theta) )+isoZ;
+
+    % Detector rotation
+    rdetY = ((detY - isoY).*cos(phi)-(detZ - isoZ).*sin(phi) )+isoY;
+    rdetZ = ((detY - isoY).*sin(phi)+(detZ - isoZ).*cos(phi) )+isoZ;
+    
+     
+    % Map detector onto XY plane(Inside proj loop in case detector rotates)
+    [detmX,detmY] = mapp2xy(tubeX,rtubeY,rtubeZ,detX,rdetY,rdetZ);
+    
+    % Z coord does not change in X direction, so we can take only one
+    % collumn of Y mapped detector boundaries
+    detmY = detmY(:,1);        
+   
+    % Detector start index and increment
+    detIstart = 1;
+    detIinc = 1;    
+    % Mapped detector length
+    deltaDetmY = detmY(detIstart+detIinc)-detmY(detIstart);
+        
+    % For each slice
+    for nz=1:nSlices
+        
+        % Flip X (Img coord is reverse to Global)
+        slice = data3d(:,end:-1:1,nz);
+          
+        % Map slice onto XY plane
+        [pixmX,pixmY] = mapp2xy(tubeX,rtubeY,rtubeZ,objX,objY,objZ(nz)); 
+        
+        %  - Z coord does not change in one slice, so we can take just one 
+        %    line of mapped coordinates per slice
+        %  - Flip X (Img coord is reverse to Global)
+        pixmX = pixmX(1,end:-1:1);
+        pixmY = pixmY(:,1);       
+        
+        % Pixel start index and increment
+        pixIstart = 1;
+        pixIinc = 1; 
+        % Mapped pixel length
+        deltaPixmX = pixmX(pixIstart+pixIinc)-pixmX(pixIstart);
+        deltaPixmY = pixmY(pixIstart+pixIinc)-pixmY(pixIstart);
+        
+        % Start pixel and detector indices
+        detIndY = detIstart;
+        pixIndY = pixIstart;                 
+        
+        % Case 1
+        % Find first detector overlap maped with pixel maped on Y
+        if(detmY(detIndY)-pixmY(pixIstart)<-deltaDetmY)
+            while(detmY(detIndY)-pixmY(pixIstart)<-deltaDetmY)            
+                detIndY = detIndY + detIinc;            
+            end
+        else
+        % Case 2    
+        % Find first pixel overlap maped with detector maped on Y            
+            if(detmY(detIstart)-pixmY(pixIndY)>deltaPixmY)
+                while(detmY(detIstart)-pixmY(pixIndY)>deltaPixmY)            
+                    pixIndY = pixIndY + pixIinc;            
+                end
+            end
+        end
+        
+        % Get the left coordinate of the first overlap on Y axis
+        % Try the following lines for a better understanding
+        % % ---------------------------------------------------------------
+        % % figure
+        % % plot(detmY(:),zeros(1,size(detmY,1)),'r.','MarkerSize',6)
+        % % hold on
+        % % plot(pixmY(:),zeros(1,size(pixmY,1)),'b.','MarkerSize',6)
+        % % hold off
+        % % legend('Detector Boundaries','Pixel Boundaries')
+        % % title('Overlap on Y axis')
+        % % ---------------------------------------------------------------
+        if( detmY(detIndY) < pixmY(pixIndY) )
+            moving_left_boundaryY = pixmY(pixIndY);            
+        else
+            moving_left_boundaryY = detmY(detIndY);
+        end       
+        
+        % Loop over Y intersections
+        while((detIndY<=nDetY)&&(pixIndY<=nPixY))
+             
+            alpha = atan((detmY(detIndY)+(deltaDetmY/2)-rtubeY)/rtubeZ);
+            
+            % Case A, when you jump to the next detector boundarie but stay
+            % in the same pixel
+            if(detmY(detIndY+1)<=pixmY(pixIndY+1))
+                overLapY = (detmY(detIndY+1)- moving_left_boundaryY)...
+                    /deltaDetmY; % Normalized overlap Calculation
+            else
+            % Case B, when you jump to the next pixel boundarie but stay 
+            % in the same detector
+                overLapY = (pixmY(pixIndY+1)- moving_left_boundaryY)...
+                    /deltaDetmY; % Normalized overlap Calculation
+            end
+                                 
+            %% X overlap    
+            detIndX = detIstart;
+            pixIndX = pixIstart; 
+            
+            % Get row/coll of X, which correspond to that Y overlap det
+            detmXrow = detmX(detIndY,end:-1:1);
+            
+            % Mapped detecor length on X
+            deltaDetmX = detmXrow(detIstart+detIinc)-detmXrow(detIstart); 
+            
+            % Case 1
+            % Find first detector overlap maped with pixel maped on X 
+            if(detmXrow(detIndX)-pixmX(pixIstart)<-deltaDetmX)
+                while(detmXrow(detIndX)-pixmX(pixIstart)<-deltaDetmX)            
+                    detIndX = detIndX + detIinc;            
+                end
+            else
+            % Case 2
+            % Find first pixel overlap maped with detector maped on X            
+                if(detmXrow(detIstart)-pixmX(pixIndX)>deltaPixmX)
+                    while(detmXrow(detIstart)-pixmX(pixIndY)>deltaPixmX)            
+                        pixIndX = pixIndX + pixIinc;            
+                    end
+                end
+            end
+
+            % Get the left coordinate of the first overlap on X axis
+            % Try the following lines for a better understanding
+            % % ---------------------------------------------------------------
+            % % figure
+            % % plot(detmXrow(:),zeros(1,size(detmXrow,1)),'r.','MarkerSize',6)
+            % % hold on
+            % % plot(pixmX(:),zeros(1,size(pixmX,1)),'b.','MarkerSize',6)
+            % % hold off
+            % % legend('Detector Boundaries','Pixel Boundaries')
+            % % title('Overlap on X axis')
+            % % ---------------------------------------------------------------
+
+            if( detmXrow(detIndX) < pixmX(pixIndX) )
+                moving_left_boundaryX = pixmX(pixIndX);            
+            else
+                moving_left_boundaryX = detmXrow(detIndX);
+            end
+        
+                
+            % Loop over X intersections
+            while((detIndX<=nDetX)&&(pixIndX<=nPixX))
+                 
+                gamma = atan((detmXrow(detIndX)+(deltaDetmX/2)-tubeX)/rtubeZ);                
+                
+                % Case A, when you jump to the next detector boundarie but stay
+                % in the same pixel
+                if(detmXrow(detIndX+1)<=pixmX(pixIndX+1))
+                    
+                    overLapX = (detmXrow(detIndX+1)- moving_left_boundaryX)...
+                    /deltaDetmX; % Normalized overlap Calculation
+                                            
+                    proj_tmp((detIndX-1)*nDetY+detIndY) = proj_tmp((detIndX-1)*nDetY+detIndY) ...
+                    + overLapX * overLapY * slice((pixIndX-1)*nPixY+pixIndY )...
+                    * pixsZ/(cos(alpha)*cos(gamma));               
+                
+                    detIndX = detIndX + detIinc;
+                    moving_left_boundaryX = detmXrow(detIndX);
+                    
+                else
+                % Case B, when you jump to the next pixel boundarie but stay 
+                % in the same detector
+                
+                    overLapX = (pixmX(pixIndX+1)- moving_left_boundaryX)...
+                    /deltaDetmX; % Normalized overlap Calculation
+                                            
+                    proj_tmp((detIndX-1)*nDetY+detIndY) = proj_tmp((detIndX-1)*nDetY+detIndY) ...
+                    + overLapX * overLapY * slice((pixIndX-1)*nPixY+pixIndY )...
+                    * pixsZ/(cos(alpha)*cos(gamma));
+                
+                    pixIndX = pixIndX + pixIinc;
+                    moving_left_boundaryX = pixmX(pixIndX);
+                    
+                end
+            end
+           
+            %% Back to Y overlap
+            
+            % Case A, when you jump to the next detector boundarie but stay
+            % in the same pixel
+            if(detmY(detIndY+1)<=pixmY(pixIndY+1))
+                detIndY = detIndY + detIinc;
+                moving_left_boundaryY = detmY(detIndY);
+            else
+            % Case B, when you jump to the next pixel boundarie but stay 
+            % in the same detector
+                pixIndY = pixIndY + pixIinc;
+                moving_left_boundaryY = pixmY(pixIndY);
+            end
+            
+        end % Y Overlap loop
+                     
+    end % Loop end slices
+    
+    proj(:,:,p) = fliplr(proj_tmp);    
+    
+end % Loop end Projections
+
+end
+%% Map on XY plane
+function [x,y] = mapp2xy(x1,y1,z1,x2,y2,z2)
+    x = x2 + z2 .* (x2-x1)./(z1-z2);
+    y = y2 + z2 .* (y2-y1)./(z1-z2);
+end

+ 240 - 0
Functions/projectionDDb.m

@@ -0,0 +1,240 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: March, 2019
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 projectionDDb(data3d,param,projNumber)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function calculates the volume projection based on the
+%     Branchless Distance-Driven principle.  
+%     The geometry is for DBT with half cone-beam. All parameters are set 
+%     in "ParameterSettings" code. 
+%  
+%     INPUT:
+% 
+%     - data3d = 3D volume for projection 
+%     - param = Parameter of all geometry
+%     - projNumber = Vector with projections numbers to be processed
+%
+%     OUTPUT:
+% 
+%     - proj = projections for each angle.
+% 
+%     Reference: 
+%     - Branchless Distance Driven Projection and Backprojection,
+%     Samit Basu and Bruno De Man (2006)
+%     - GPU Acceleration of Branchless Distance Driven Projection and 
+%     Backprojection, Liu et al (2016)
+%     - GPU-Based Branchless Distance-Driven Projection and Backprojection,
+%     Liu et al (2017)
+%     - A GPU Implementation of Distance-Driven Computed Tomography, 
+%     Ryan D. Wagner (2017)
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2019>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% 3-D Projection Branchless Distance Driven Code
+function proj = projectionDDb(data3d,param,projNumber)
+
+% Map detector and object boudaries
+param.us = double((param.nu:-1:0)*param.du);
+param.vs = double((-(param.nv)/2:1:(param.nv)/2)*param.dv);
+param.xs = double((param.nx:-1:0)*param.dx);
+param.ys = double((-(param.ny)/2:1:(param.ny)/2)*param.dy);
+param.zs = double((0:1:param.nz-1)*param.dz + param.DAG + (param.dz/2));
+
+% Detector boudaries coordinate sytem in (mm)
+[detX,detY] = meshgrid(param.us,param.vs);
+detZ = zeros(size(detX),'double');
+
+% Object boudaries coordinate sytem in (mm)
+[objX,objY] = meshgrid(param.xs,param.ys);
+objZ = double(param.zs);     % Z object coordinates
+
+% X-ray tube initial position
+tubeX = double(0);
+tubeY = double(0);
+tubeZ = double(param.DSD);
+
+% Iso-center position
+isoY = double(0);
+isoZ = double(param.DDR);
+
+% Projection angles
+tubeAngle = double(deg2rad(param.tubeDeg));
+detAngle = double(deg2rad(param.detectorDeg));
+
+% Number of detector and voxels for each direction
+nDetX = double(param.nu);
+nDetY = double(param.nv);
+nPixX = double(param.nx);
+nPixY = double(param.ny);
+
+deltaDetX = double(param.du); 
+deltaDetY = double(param.dv); 
+deltaObjX = double(param.dx); 
+deltaObjY = double(param.dy);
+
+nSlices = double(param.nz);
+nProjs = double(param.nProj);
+
+% Voxel size on Z
+pixsZ = double(param.dz); 
+
+% Test if there's specific angles
+if(isempty(projNumber))
+    projNumber = 1:nProjs;
+else
+    if(max(projNumber(:)) <= nProjs)
+        nProjs = size(projNumber,2);
+    else
+        error('Projection number exceeds the maximum for the equipment.')
+    end
+end
+
+% Stack of projections
+proj = zeros(param.nv, param.nu, nProjs,'double');
+
+% Integration of 2D slices over the whole volume 
+% (S.1. Integration. - Liu et al (2017))
+data3dI = integrate2D(data3d);
+
+% For each projection
+for p=1:nProjs
+    
+    % Temporary variable to acumulate all projection from the slices
+    proj_tmp = zeros(nDetY,nDetX,'double');
+    
+    % Get specific projection number
+    projN = projNumber(p);
+    
+    % Get specif tube angle for the projection
+    theta = tubeAngle(projN);
+    
+    % Get specif detector angle for the projection
+    phi = detAngle(projN);
+    
+    % Tubre rotation
+    rtubeY = ((tubeY - isoY)*cos(theta)-(tubeZ - isoZ)*sin(theta) )+isoY;
+    rtubeZ = ((tubeY - isoY)*sin(theta)+(tubeZ - isoZ)*cos(theta) )+isoZ;
+
+    % Detector rotation
+    rdetY = ((detY - isoY).*cos(phi)-(detZ - isoZ).*sin(phi) )+isoY;
+    rdetZ = ((detY - isoY).*sin(phi)+(detZ - isoZ).*cos(phi) )+isoZ;
+    
+    
+    % For each slice
+    for nz=1:nSlices
+
+        % Map detector onto XY plane in the specifc slice   
+        [detmX,detmY] = mapp2xyZ(tubeX,rtubeY,rtubeZ,detX,rdetY,rdetZ,objZ(nz));
+        
+        % Bilinear interpolation of the integrated image(slice coords) on
+        % mapped detector coords
+        % (S.2. Interpolation - Liu et al (2017))
+        proj_data3dI = interp2(objX,objY,data3dI(:,:,nz),detmX,detmY,'linear',0);
+        
+        %figure
+        %plot(detmX,detmY,'k*','MarkerSize',6)
+        %hold on
+        %plot(objX,objY,'k.','MarkerSize',6)
+        %hold off
+        %legend('Detector Boundaries','Pixel Boundaries')
+        
+        % These two lines find the max value of the integrated image, in 
+        % order to replicate it in the remaining rows
+        [~,row_max] = max(proj_data3dI(:,end));   
+        proj_data3dI(row_max+1:end,:) = repmat(proj_data3dI(row_max,:),param.nv+1-row_max,1);
+        
+        for xDet=2:nDetX+1
+              
+            % x-ray angle in X coord
+            gamma = atan((detX(1,xDet-1)+(deltaDetX/2)-tubeX)/rtubeZ); 
+            
+            for yDet=2:nDetY+1
+                
+                % Detector X coord overlap calculation
+                overLapX = detmX(yDet,xDet-1)-detmX(yDet,xDet);
+                
+                % Detector Y coord overlap calculation
+                overLapY = detmY(yDet,1)-detmY(yDet-1,1);
+                
+                 % x-ray angle in Y coord
+                alpha = atan((rdetY(yDet-1,1)+(deltaDetY/2)-rtubeY)/rtubeZ);              
+                             
+                % S.3. Differentiation - Eq. 24 - Liu et al (2017)
+                proj_tmp(yDet-1,xDet-1) = proj_tmp(yDet-1,xDet-1)...
+                                         +((proj_data3dI(yDet,xDet)...
+                                         -proj_data3dI(yDet,xDet-1)...
+                                         -proj_data3dI(yDet-1,xDet)...
+                                         +proj_data3dI(yDet-1,xDet-1))...
+                                         *(deltaObjX*deltaObjX.*pixsZ./(cos(alpha)*cos(gamma)*overLapX*overLapY)));                                                                              
+            end % Loop end Y coord
+        end % Loop end X coord
+    
+    end % Loop end slices
+   
+    proj(:,:,p) = proj_tmp;
+
+end % Loop end Projections
+end
+
+%% Integrate function
+function [Ppj] = integrate2D (imageStack)
+
+%         2-D image
+%
+%     | -> J
+%     v -------------
+%     I |           |
+%       |           |
+%       |           |
+%       |           |
+%       -------------
+
+ni_pixel = size(imageStack,1)+1;
+nj_pixel = size(imageStack,2)+1;
+n_stack = size(imageStack,3);
+
+p_v = zeros(ni_pixel,nj_pixel,n_stack,'double');    % Pad with zeros
+p_v(2:end,2:end,:) = imageStack;            % Load slices
+
+P_i = zeros(ni_pixel,1,n_stack,'double');
+P_j = zeros(1,nj_pixel,n_stack,'double');
+
+Ppj = zeros(ni_pixel,nj_pixel,n_stack,'double');
+
+% Integrate on J direction
+for pj=2:nj_pixel    
+   P_i = P_i + p_v(:,pj,:);
+   Ppj(:,pj,:) = P_i;
+end
+
+% Integrate on I direction
+for pi=2:ni_pixel    
+   P_j = P_j + Ppj(pi,:,:);
+   Ppj(pi,:,:) = P_j;
+end
+
+end
+
+%% Map on XY plane (on specific slice)
+function [x,y] = mapp2xyZ(x1,y1,z1,x2,y2,z2,z)
+    x = ((x2-x1).*(z-z2)-(x2.*z1)+(x2.*z2))./(-z1+z2);
+    y = ((y2-y1).*(z-z2)-(y2.*z1)+(y2.*z2))./(-z1+z2);
+end

+ 147 - 0
Functions/projectionMicro.m

@@ -0,0 +1,147 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: April, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                 projection(data3d,param)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function calculates for each detector pixel, which voxel is
+%     associated with that pixel in the specific projection. That is done 
+%     for all angles specified.
+%     The geometry is for DBT with half cone-beam. All parameters are set 
+%     in "ParameterSettings" code. 
+%  
+%     INPUT:
+% 
+%     - data3d = 3D volume for projection 
+%     - param = Parameter of all geometry
+%     - projNumber = Vector with projections numbers to be processed
+%
+%     OUTPUT:
+% 
+%     - proj = projection for each angle.
+% 
+%     Reference: Patent US5872828
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% Projection Code
+function proj = projection(microData,param)
+
+global animation
+
+% Get parameters from struct
+DSR = param.DSR;
+DDR = param.DDR;
+tubeAngle = deg2rad(param.tubeDeg);
+numVPixels = param.nv;
+numUPixels = param.nu;
+numYVoxels = param.ny;
+numXVoxels = param.nx;
+numSlices = param.nz;
+numProjs = param.nProj;
+zCoords = param.zs;             % Z object coordinates
+
+
+
+projNumber = 1:numProjs;
+
+
+% Stack of projections
+proj = zeros(param.nv, param.nu, numProjs,'single');
+
+% Detector Coordinate sytem in (mm)
+[uCoord,vCoord] = meshgrid(param.us,param.vs);
+
+% Object Coordinate sytem in (mm) (just for 3D visualization)
+[xCoord,yCoord] = meshgrid(param.xs,param.ys);
+[   ~  ,zCoord] = meshgrid(param.ys,param.zs);
+
+fprintf('%2d/%2d', 1, numProjs)
+
+% For each projection
+for p=1:numProjs
+
+    fprintf('\b\b\b\b\b%2d/%2d', p, numProjs)
+
+    % Get specific projection number
+    projN = projNumber(p);
+    
+    % Get specific tube angle for the projection
+    teta = tubeAngle(projN);
+    
+    % Temporary projection variable to acumulate all projection from the slices
+    proj_tmp = zeros(numVPixels,numUPixels,'single');
+    
+    % For each slice
+    for nz=1:numSlices
+          
+        % Calculates a relation of detector and voxel coordinates
+        pyCoord = ((vCoord.*((DSR.*cos(teta))+DDR-zCoords(nz)))-(zCoords(nz).*DSR.*sin(teta)))./...
+                                            (DSR.*cos(teta)+DDR);
+        
+        pxCoord = (uCoord.*((DSR.*cos(teta))+DDR-zCoords(nz)))./...
+                             ((DSR.*cos(teta))+DDR);          
+        
+        
+             
+             
+        % Coordinate in pixel of slice plane axis origin
+        x0 = numXVoxels;
+        y0 = numYVoxels/2;
+        
+        % Represent slice plane axis (X,Y) in the image plane axis (i,j) (covert mm to Pixels)
+        pxCoord = -(pxCoord./param.dx) + x0;
+        pyCoord =  (pyCoord./param.dy) + y0;  
+      
+        % Interpolation of the pixel coordinates of the "Slice" at the calculated pixel coordinates for the detector
+        proj_tmp = proj_tmp + interp2(microData(:,:,nz),pxCoord,pyCoord,'linear',0);
+              
+    end % Loop end slices
+    
+    % Converting attenuation coefficients to signal image + adding noise
+    proj(:,:,p) = exp(- param.dz * proj_tmp);
+
+                                                                   
+figure(1)
+% Calculus of projection on the detector for visualization
+pvCoord = yCoord+((zCoords(nz).*((DSR.*sin(teta))+ yCoord))./...
+                     ((DSR.*cos(teta))-zCoords(nz)));
+
+puCoord = (xCoord.*DSR.*cos(teta))./ ...
+       ((DSR.*cos(teta))-zCoords(nz));
+   
+% Draw 3D animation   
+draw3d(xCoord,yCoord,zCoord,puCoord,pvCoord,param,projN,teta);
+
+
+figure(1)
+subplot(2,2,2)
+imshow(imrotate(microData(:,:,round(numSlices/2)),90),[])
+title('Slice');axis on;
+subplot(2,2,4)
+imshow(imrotate(proj(:,:,p),90),[]); title(['Projection ',num2str(projN)]);axis on;
+
+
+
+   
+    
+end % Loop end Projections
+end

+ 80 - 0
Functions/readDicom.m

@@ -0,0 +1,80 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: May, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                         readDicom(imgdir,parameter,answer)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function read a set of Dicom images from a directory.
+%   
+%     INPUT:
+%     - imgdir = Directory for Dicom files 
+%     - parameter = Parameter of all geometry 
+% 
+%     OUTPUT:
+%     - dataDicom = Stack of Dicom images.
+%     - infoDicom = Stack of Dicom headers.
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% Read Dicom images from files
+function [dataDicom,infoDicom] = readDicom(imgdir,parameter,answer)
+
+if(strcmp(parameter.type,'vct'))
+    dc = 1;
+    typecase = 1;
+end
+if(strcmp(parameter.type,'hologic'))
+    dc = 1;
+    typecase = 0;
+end
+if(strcmp(parameter.type,'ge'))
+    dc = 0;
+    typecase = 1;
+end
+if(strcmp(answer,'Microcalcifications') || strcmp(answer,'Noised'))
+    dc = 0;
+    typecase = 0;
+end
+
+img_list = dir([imgdir,filesep,'*.dcm']);  % List dicom files
+img_count = size(img_list,1);
+
+fprintf('%2d/%2d', 1, img_count)
+
+for i=1:img_count
+    fprintf('\b\b\b\b\b%2d/%2d', i, img_count)
+    imgAux = single(dicomread([imgdir, filesep, img_list(i,:).name]));  % Read Dicom
+    infoAux = dicominfo([imgdir, filesep, img_list(i,:).name]);         % Read Dicom headers
+    
+    if(typecase == 1)
+        nProj = infoAux.InstanceNumber+dc;
+    else
+        filename = strtrim(img_list(i,:).name);
+        filenameSlpited = strsplit(filename, '_');
+        filenameSlpited = strsplit(filenameSlpited{end}, '.');
+        nProj = str2double(filenameSlpited{1})+dc;
+    end 
+
+    dataDicom(:,:,nProj) = imgAux;
+    infoDicom(:,nProj) = infoAux;
+end
+
+end

+ 85 - 0
Functions/saveData.m

@@ -0,0 +1,85 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: Jun, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%           saveData(dataRecon3d,parameter,title,folder,infoDicom)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function save projections of a virtual phantom or
+%     reconstructed data.
+%  
+%  
+%     INPUT:
+%     - dataRecon3d = Reconstructed data
+%     - parameter = Parameter of all geometry
+%     - title = Title of the data (Dicom, Shepp-Logan, VCT)
+%     - infoDicom = Dicom header from projections
+%     - folder = Folder to save the data
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% Save data
+function saveData(dataRecon3d,varargin)
+
+optionals = {[],[],[],[]}; % Placeholder for inputs
+
+numInputs = nargin - 1; % Minus number of required inputs
+inputVar = 1;
+
+while numInputs > 0
+    if ~isempty(varargin{inputVar})
+        optionals{inputVar} = varargin{inputVar};
+    end
+    inputVar = inputVar + 1;
+    numInputs = numInputs - 1;
+end
+
+parameter = optionals{1};
+title = optionals{2};
+folder = optionals{3};
+infoDicom = optionals{4};
+
+if(isempty(folder)) 
+    folder='Output';
+else
+    mkdir([folder,filesep,title]);
+end
+
+% Some more informations to save
+for k=1:size(dataRecon3d,2)
+    info = dataRecon3d{2,k};
+    info.Title = title;
+    dataRecon3d{2,k} = info;
+end
+
+filestring = [folder,filesep,title,filesep,'Reconstruction-',info.reconMeth];
+
+% Dicom breast/virtual phantom images
+if(~isempty(infoDicom))
+    fprintf('\nWriting images   ')
+    writeDicom(dataRecon3d,infoDicom,filestring,info.reconMeth,parameter);
+else
+    writeDcm(dataRecon3d,filestring,parameter)
+end
+
+fprintf('\nSaving data...')
+save(filestring,'dataRecon3d','-v7.3')
+
+end

+ 49 - 0
Functions/writeDcm.m

@@ -0,0 +1,49 @@
+%% Author: Aleks Prša
+% Date: May, 2022
+% alex.prsa@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                   writeDcm(dataRecon3d,filestring,parameter)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function write a set of Dicom images with respective headers.
+%  
+%  
+%     INPUT:
+%     - dataRecon3d = Reconstructed data
+%     - filestring = Path to save
+%     - parameter = Parameter of all geometry
+% 
+%     ---------------------------------------------------------------------
+%}
+% =========================================================================
+%% Write Dicom images
+function writeDcm(dataRecon3d,filestring,parameter)
+
+if(~exist(filestring))
+    mkdir(filestring)
+end
+
+for k=1:size(dataRecon3d,2)
+
+    dataRecon3d{1,k} = dataRecon3d{1,k} + abs(min(dataRecon3d{1,k}(:)));
+    dataRecon3d{1,k} = (2^16-1).*mat2gray(dataRecon3d{1,k});
+    dataRecon3d{1,k} = uint16(dataRecon3d{1,k});
+
+end
+
+% Write dicom files in respective folder
+fprintf('\nWriting images   ')
+fprintf('%3d/%3d', 1, parameter.nz)
+for i=1:size(dataRecon3d{1,k},3)
+    fprintf('\b\b\b\b\b\b\b%3d/%3d', i, parameter.nz)
+    if(i<10)
+        fileimg = [filestring,filesep,'0',num2str(i),'.dcm'];
+    else
+        fileimg = [filestring,filesep,num2str(i),'.dcm'];
+    end
+    dicomwrite(dataRecon3d{1,k}(:,:,i), fileimg);
+end
+
+end

+ 40 - 0
Functions/writeDcmProj.m

@@ -0,0 +1,40 @@
+%% Author: Aleks Prša
+% Date: May, 2022
+% alex.prsa@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                     writeDcmProj(dataProj,filestring,parameter)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function write a set of Dicom images with respective headers.
+%  
+%  
+%     INPUT:
+%     - dataProj = Projection images
+%     - filestring = Path to save
+%     - parameter = Parameter of all geometry
+% 
+%     ---------------------------------------------------------------------
+%}
+% =========================================================================
+%% Write Dicom images
+function writeDcm(dataProj,filestring,parameter)
+
+for k=1:size(dataProj,3)
+    dataProj = uint16(dataProj);
+end
+
+% Write dicom files in respective folder
+fprintf('%3d/%3d', 1, parameter.nProj)
+for i=1:size(dataProj,3)
+    fprintf('\b\b\b\b\b\b\b%3d/%3d', i, parameter.nProj)
+    if(i<10)
+        fileimg = [filestring,filesep,'0',num2str(i),'.dcm'];
+    else
+        fileimg = [filestring,filesep,num2str(i),'.dcm'];
+    end
+    dicomwrite(dataProj(:,:,i), fileimg);
+end
+
+end

+ 144 - 0
Functions/writeDicom.m

@@ -0,0 +1,144 @@
+%% Author: Rodrigo de Barros Vimieiro / Aleks Prša
+% Date: April, 2018 / May 2022
+% rodrigo.vimieiro@gmail.com / alex.prsa@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%    writeDicom(dataRecon3d,infoDicomRecon,filestring,reconmeth,parameter)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function write a set of Dicom images with respective headers.
+%  
+%     INPUT:
+%     - dataRecon3d = Reconstructed data
+%     - infoDicomProjs = Dicom header from projections
+%     - filestring = Path to save
+%     - reconmeth = Reconstructed method
+%     - parameter = Parameter of all geometry
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% Write Dicom images
+function writeDicom(dataRecon3d,infoDicomRecon,filestring,reconmeth,parameter)
+
+% Get reconstructed dicom headers
+ %uiwait(msgbox('Select reconstructed image Dicom file to get headers.','Dicom','Warn'));
+ %[filename,path] = uigetfile('*.dcm');
+ %infoDicomRecon = dicominfo([path,filename]);
+
+% Weather is Hologic or GE
+dc=0;
+if(strcmp(parameter.type,'vct')||strcmp(parameter.type,'hologic'))
+    dc = -1;
+end
+
+% Adjust data to save, based on the recon method
+if(strcmp(reconmeth,'BP')||strcmp(reconmeth,'MLEM')||strcmp(reconmeth,'SART')||strcmp(reconmeth,'SIRT'))
+    for k=1:size(dataRecon3d,2)
+        dataRecon3d{1,k} = (2^16-1).*mat2gray(dataRecon3d{1,k});
+        dataRecon3d{1,k} = uint16(dataRecon3d{1,k});
+    end
+else
+    if(strcmp(reconmeth,'FBP'))
+        fprintf('\nAdjusting grayscale - windowing   ')
+        for k=1:size(dataRecon3d,2)
+            dataRecon3d{1,k} = dataRecon3d{1,k} + abs(min(dataRecon3d{1,k}(:)));
+            dataRecon3d{1,k} = (2^16-1).*mat2gray(dataRecon3d{1,k});
+            dataRecon3d{1,k} = uint16(dataRecon3d{1,k});
+
+            % Adjusting grayscale window - windowing            
+            data = dataRecon3d{1,k};
+            data = reshape(data,1,numel(data));
+            data = data ./ 256;
+            data = fix(data);
+            nmin = 0;
+            nmax = 0;
+            binmin = 0;
+            binmax = 0;
+            
+            % Determining lower grayscale limit
+            fprintf('\n   Lower limit')
+            for bin=1:256
+                for i=1:numel(data)
+                    if(data(i) == (bin-1))
+                        nmin = nmin + 1;
+                    end
+                    if(nmin > 14739305)
+                        binmin = bin;
+                        break
+                    end
+                end
+                if(nmin > 14739305)
+                    binmin = bin;
+                    break
+                end
+            end
+
+            % Determining upper grayscale limit
+            fprintf('\n   Upper limit')
+            for bin=1:256
+                for i=1:numel(data)
+                    if(data(i) == (257-bin))
+                        nmax = nmax + 1;
+                    end
+                    if(nmax > 37331)
+                        binmax = bin;
+                        break
+                    end
+                end
+                if(nmax > 37331)
+                    binmax = bin;
+                    break
+                end
+            end
+
+            wmin = binmin*256;                                  % Lover grayscale limit
+            wmax = (256-binmax)*256;                            % Upper grayscale limit
+
+            dataRecon3d{1,k}(dataRecon3d{1,k} < wmin) = wmin;
+            dataRecon3d{1,k}(dataRecon3d{1,k} > wmax) = wmax;
+
+            %dataRecon3d{1,k}(dataRecon3d{1,k} < 32325) = 32325;
+            %dataRecon3d{1,k}(dataRecon3d{1,k} > 58600) = 58600;          
+
+            dataRecon3d{1,k} = (2^16-1).*mat2gray(dataRecon3d{1,k});
+            dataRecon3d{1,k} = uint16(dataRecon3d{1,k});
+        end
+    else
+        error('Invalid recon method');
+    end
+end
+
+if(~exist(filestring))
+    mkdir(filestring)
+end
+
+% Write Dicom files in respective folder
+fprintf('\nWriting DICOMs   ')
+fprintf('%3d/%3d', 1, parameter.nz)
+for i=1:size(dataRecon3d{1,1},3)
+    fprintf('\b\b\b\b\b\b\b%3d/%3d', i, parameter.nz)
+    if(i<10)
+        fileimg = [filestring,filesep,'0',num2str(i+dc),'.dcm'];
+    else
+        fileimg = [filestring,filesep,num2str(i+dc),'.dcm'];
+    end
+    dicomwrite(dataRecon3d{1,1}(:,:,i), fileimg, infoDicomRecon(:,1), 'CreateMode', 'copy');
+end
+
+end

+ 42 - 0
Functions/writeDicomProj.m

@@ -0,0 +1,42 @@
+%% Author: Aleks Prša
+% Date: May, 2022
+% alex.prsa@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                     writeDicomProj(dataProj,infoDicom,filestring,parameter)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function write a set of Dicom images with respective headers.
+%  
+%  
+%     INPUT:
+%     - dataProj = Projection images
+%     - infoDicomProjs = Dicom header from projections
+%     - filestring = Path to save
+%     - parameter = Parameter of all geometry
+% 
+%     ---------------------------------------------------------------------
+%}
+% =========================================================================
+%% Write Dicom images
+function writeDicomProj(dataProj,infoDicom,filestring,parameter)
+
+for k=1:size(dataProj,3)
+    dataProj = 65535*mat2gray(dataProj);
+    dataProj = uint16(dataProj);
+end
+
+% Write dicom files in respective folder
+fprintf('%3d/%3d', 1, parameter.nProj)
+for i=1:size(dataProj,3)
+    fprintf('\b\b\b\b\b\b\b%3d/%3d', i, parameter.nProj)
+    if(i<10)
+        fileimg = [filestring,filesep,'0',num2str(i),'.dcm'];
+    else
+        fileimg = [filestring,filesep,num2str(i),'.dcm'];
+    end
+    dicomwrite(dataProj(:,:,i), fileimg, infoDicom(:,1), 'CreateMode', 'copy');
+end
+
+end

+ 674 - 0
LICENSE

@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program 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.
+
+    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.

+ 107 - 0
MLEM.m

@@ -0,0 +1,107 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: April, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                         MLEM(proj,nIter,parameter)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function reconstruct iteratively the volume through 
+%     Maximum-Likelihood Expectation-Maximization (MLEM) method.
+%     
+%     The geometry is for DBT with half cone-beam. All parameters are set 
+%     in "ParameterSettings" code. 
+%  
+%     INPUT:
+% 
+%     - proj  = 2D projection images 
+%     - nIter = Specific iterations to save volume data 
+%     - parameter = Parameter of all geometry
+% 
+%     OUTPUT:
+% 
+%     - allreconData3d{1,...} = Cell with Volumes of each specific iteration.
+%     - allreconData3d{2,...} = Cell with informations of each specific iteration.
+% 
+%     Reference: Sidky, E. Y., 8-Iterative image reconstruction design 
+%     for digital breast tomosynthesis, Tomosynthesis Imaging (2014).
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% Recon Code -  Iterative reconstruction: MLEM
+function allreconData3d = MLEM(proj,nIter,parameter)
+
+global showinfo
+
+info.startDateAndTime = char(datetime('now','Format','MM-dd-yyyy''  ''HH:mm:ss'));
+info.reconMeth = 'MLEM';
+
+highestValue = (2^parameter.bitDepth) - 1;
+
+allIterTime = zeros(nIter(end),1);   % Time data for each iteration
+allreconData3d = cell(1,size(nIter,2)); % Recon data for each iteration
+
+% Initial estimated volume data 
+reconData3d = zeros(parameter.ny, parameter.nx, parameter.nz,'single');
+reconData3d(:) = 1;
+
+% Volume normalization
+vol_norm = backprojection(ones(parameter.nv, parameter.nu, parameter.nProj, 'single'), parameter,[]);
+
+if(showinfo)
+    fprintf('----------------\nStarting MLEM Iterations... \n\n')
+end
+
+% Start Iterations
+for iter = 1:nIter(end)
+    
+    tStart = tic;
+    
+    % Error ratio between raw data and projection of estimated data  
+    proj_ratio = proj./projection(reconData3d,parameter,[]);
+    proj_ratio(isnan(proj_ratio)) = 0;
+    proj_ratio(isinf(proj_ratio)) = 0;
+
+    upt_term = backprojection(proj_ratio, parameter,[]);
+    upt_term = upt_term ./vol_norm; % Volume normalization
+    upt_term(isnan(upt_term)) = 0;  
+    upt_term(isinf(upt_term)) = 0;
+
+    reconData3d = reconData3d.*upt_term; % Updates the previous estimation 
+        
+    % Truncate to highest value
+    reconData3d(reconData3d>highestValue) = highestValue;
+    
+    allIterTime(iter,1) = toc(tStart);
+    
+    % Save data 
+    indIter = find(nIter == iter);
+    if(indIter~=0)
+        allreconData3d{1,indIter} = reconData3d(parameter.iROI,parameter.jROI,parameter.sliceRange);
+        info.IterationNumber = num2str(iter);
+        info.IterationTime = num2str(sum(allIterTime(:)));
+        allreconData3d{2,indIter} = info;        
+    end
+    
+    if(showinfo) 
+        fprintf('Iteration %d Time: %.2f\n\n',iter,allIterTime(iter,1));  
+    end
+    
+end% Loop end iterations
+end

+ 86 - 0
Microcalcification.m

@@ -0,0 +1,86 @@
+%% Author: Aleks Prša
+% Date: June, 2022
+% alex.prsa@gmail.com
+% =========================================================================
+%{
+% 
+%     DESCRIPTION:
+% 
+%     Program for inserting microcalcifications on breast projection images.
+%
+%     Faculty of Mathematic and Physics
+%     University in Ljubljana
+%     Ljubljana, Slovenia   
+%  
+%}
+% =========================================================================
+%%                      Insertion code
+
+close all;clear;clc
+
+%% Load components
+
+addpath(genpath('Functions'));
+addpath(genpath('Parameters'));
+
+if(~exist('Output','dir'))
+    mkdir('Output')
+end
+
+addpath('Output');
+
+ParameterSettings_MicroInsertion
+       
+% Load projection data    
+path_User = userpath;
+path_ProjData = uigetdir(path_User); 
+
+if(path_ProjData == 0)
+    fprintf('Cancelled by user \n');
+	return;
+else
+    userpath(path_ProjData)       
+    fprintf('Reading DICOMs   ')
+    answer = '0';
+    [dataProj,infoDicom] = readDicom(path_ProjData,parameter,answer);
+    parameter.bitDepth = infoDicom(:,1).BitDepth;
+end
+
+% Create phantom with inserted microcalcifications
+fprintf('\nPhantom...')
+microData = phantom(parameter);
+microData(microData<0) = eps;
+
+microData = flip(microData, 1);                                % Phantom in accordance with coordinate system
+microData = flip(microData, 2);                                % Source is on the left side
+
+% Perform virtual imaging of microcalcifications
+fprintf('\nProjections   ')
+attenuationMicro = projectionMicro(microData,parameter);
+attenuationMicro = flip(attenuationMicro, 2);                  % Image captured with source on the right side
+
+%% Microcalcification insertion 
+
+% Breast attenuation
+attenuationBreast = dataProj - parameter.mu;
+attenuationBreast(attenuationBreast < 0) = 0;
+
+% Additional attenuation due to microcalcifications
+microProj = attenuationBreast .* attenuationMicro + parameter.mu;
+
+%% Save data
+
+filestring = ['Output',filesep,'Microcalcifications',filesep,'Projections'];   
+    if(~exist(filestring))
+        mkdir(filestring)
+    end
+fprintf('\nWriting DICOM projections   ')
+writeDicomProj(attenuationMicro,infoDicom,filestring,parameter);
+
+fprintf('\nSaving projections... \n')
+save(['Output',filesep,'Microcalcifications',filesep,'Microcalcifications.mat'],'microProj','-v7.3');
+infoDicom = [];
+
+fprintf('\nFINISHED - Data stored in "Microcalcification" folder\n')
+
+beep

+ 21 - 0
Mikrokalcinacije.txt

@@ -0,0 +1,21 @@
+mu(/mm)	x(pix)	y(pix)	z(n)	d(um)
+0.66	80	120	27	600
+0.66	160	220	27	600
+0.66	400	190	27	600
+0.66	520	300	27	600
+0.66	80	470	27	600
+0.66	380	440	27	600
+0.66	810	450	27	600
+0.66	630	500	27	600
+0.66	240	580	27	600
+0.66	120	665	27	600
+0.66	520	600	27	600
+0.66	880	590	27	600
+0.66	700	690	27	600
+0.66	775	710	27	600
+0.66	340	745	27	600
+0.66	220	820	27	600
+0.66	430	920	27	600
+0.66	190	1100	27	600
+0.66	320	1195	27	600
+0.66	420	1130	27	600

+ 93 - 0
Parameters/ParameterSettings_GE.m

@@ -0,0 +1,93 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: April, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% 
+%     DESCRIPTION:
+%     This is the configuration file for the tomosynthesis acquisition 
+%     geometry. This geometry is specified for the GE Senographe Essential.
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. 
+%  
+%}
+% =========================================================================
+%% Physical Parameters Settings
+
+parameter.type = 'ge';
+
+% Breast voxels density
+parameter.nx = 1024;    % number of voxels (columns)
+parameter.ny = 1421;    % number of voxels (rows)
+parameter.nz = 57;      % number of voxels (slices)
+
+% Detector panel pixel density
+parameter.nu = 1500;    % number of pixels (columns)
+parameter.nv = 3000;    % number of pixels (rows)
+
+% Single voxel real size (mm)
+parameter.dx = 0.085; 
+parameter.dy = 0.085;
+parameter.dz = 1;
+
+% Single detector real size (mm)
+parameter.du = 0.085;   
+parameter.dv = 0.085;
+
+% X-ray source and detector distances
+parameter.DSD = 650;                            % Distance from source to detector (mm)
+parameter.DSO = 575;                            % Distance from source to the top of object (mm)
+parameter.DDR = 50;                             % Distance from detector to pivot (mm)
+parameter.DSR = parameter.DSD - parameter.DDR;  % Distance from source to pivot (mm)
+parameter.DAG = 19.95;                             % Distance of Air Gap (mm)    
+
+% Detector and object full real sizes (mm)
+parameter.sx = parameter.nx.*parameter.dx;  
+parameter.sy = parameter.ny.*parameter.dy;  
+parameter.sz = (parameter.nz.*parameter.dz)+parameter.DAG;  
+parameter.su = parameter.nu.*parameter.du;	
+parameter.sv = parameter.nv.*parameter.dv;  
+
+% Detector and object Volume grid settings 
+parameter.xs = (parameter.nx-1:-1:0)*parameter.dx;
+parameter.ys = (-(parameter.ny-1)/2:1:(parameter.ny-1)/2)*parameter.dy;
+parameter.zs = (0:1:parameter.nz-1)*parameter.dz + parameter.DAG;
+parameter.us = (parameter.nu-1:-1:0)*parameter.du;
+parameter.vs = (-(parameter.nv-1)/2:1:(parameter.nv-1)/2)*parameter.dv;
+
+% Number of Projections
+parameter.nProj = 25;  
+
+% Angle settings (Degrees)
+parameter.tubeAngle = 50;   % Tube Angle
+parameter.tubeDeg = linspace(parameter.tubeAngle/2,-parameter.tubeAngle/2,parameter.nProj);
+
+parameter.detAngle = 0;   % Detector Angle
+parameter.detectorDeg = linspace(-parameter.detAngle/2,parameter.detAngle/2,parameter.nProj);
+
+%% General parameters
+
+% Slice range to be saved
+parameter.sliceRange = 1:parameter.nz; 
+
+% Region of interest (ROI) to store
+parameter.iROI = 1:parameter.ny;     
+parameter.jROI = 1:parameter.nx;      
+
+% Bit number quatization
+parameter.bitDepth = [];     % Load from dicom header
+ 

+ 92 - 0
Parameters/ParameterSettings_Hologic.m

@@ -0,0 +1,92 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: November, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+
+    DESCRIPTION:
+    This is the configuration file for the tomosynthesis acquisition 
+    geometry. This geometry is specified for the Hologic Selenia Dimensions
+
+    -----------------------------------------------------------------------
+    Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+
+    This program 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.
+
+    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. 
+ 
+%}
+% =========================================================================
+%% Physical Parameters Settings
+
+parameter.type = 'hologic';
+
+% Breast voxels density
+parameter.nx = 1996;    % number of voxels (columns)
+parameter.ny = 2457;    % number of voxels (rows)
+parameter.nz = 78;     	% number of voxels (slices)
+
+% Detector panel pixel density
+parameter.nu = 1664;    % number of pixels (columns)
+parameter.nv = 2048;    % number of pixels (rows)
+
+% Single voxel real size (mm)
+parameter.dx = 0.112; 
+parameter.dy = 0.112;
+parameter.dz = 1;
+
+% Single detector real size (mm)
+parameter.du = 0.14;   
+parameter.dv = 0.14;
+
+% X-ray source and detector distances
+parameter.DSD = 700;                            % Distance from source to detector (mm)
+parameter.DSO = 597;                            % Distance from source to the top of object (mm)
+parameter.DDR = 0;                              % Distance from detector to pivot (mm)
+parameter.DSR = parameter.DSD - parameter.DDR;  % Distance from source to pivot (mm)
+parameter.DAG = 25;                             % Distance of Air Gap (mm)    
+
+% Detector and object full real sizes (mm)
+parameter.sx = parameter.nx.*parameter.dx;  
+parameter.sy = parameter.ny.*parameter.dy;  
+parameter.sz = (parameter.nz.*parameter.dz)+parameter.DAG;  
+parameter.su = parameter.nu.*parameter.du;	
+parameter.sv = parameter.nv.*parameter.dv;  
+
+
+% Detector and object Volume grid settings 
+parameter.xs = (parameter.nx-1:-1:0)*parameter.dx;
+parameter.ys = (-(parameter.ny-1)/2:1:(parameter.ny-1)/2)*parameter.dy;
+parameter.zs = (0:1:parameter.nz-1)*parameter.dz + parameter.DAG;
+parameter.us = (parameter.nu-1:-1:0)*parameter.du;
+parameter.vs = (-(parameter.nv-1)/2:1:(parameter.nv-1)/2)*parameter.dv;
+
+% Number of Projections
+parameter.nProj = 15;  
+
+% Angle settings (Degrees)
+parameter.tubeAngle = 15;   % Tube Angle
+parameter.tubeDeg = linspace(-parameter.tubeAngle/2,parameter.tubeAngle/2,parameter.nProj);
+
+parameter.detAngle = 4.2;   % Detector Angle
+parameter.detectorDeg = linspace(-parameter.detAngle/2,parameter.detAngle/2,parameter.nProj);
+
+%% General parameters
+
+% Slice range to be saved
+parameter.sliceRange = 1:parameter.nz; 
+% Region of interest (ROI) to store
+parameter.iROI = 1:parameter.ny;     
+parameter.jROI = 1:parameter.nx;      
+
+% Bit number quatization
+parameter.bitDepth = [];     % Load from dicom header

+ 106 - 0
Parameters/ParameterSettings_MicroInsertion.m

@@ -0,0 +1,106 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: April, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% 
+%     DESCRIPTION:
+%     This is the configuration file for the tomosynthesis acquisition 
+%     geometry. This geometry is specified for the virtual phantom.
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. 
+%  
+%}
+% =========================================================================
+%% Physical Parameters Settings
+
+parameter.type = 'ge';
+
+% Breast voxels density 
+parameter.nx = 1421;    % number of voxels (columns)
+parameter.ny = 1421;    % number of voxels (rows)
+parameter.nz = 500;     % number of voxels (slices)
+
+% Detector panel pixel density
+parameter.nu = 1500;    % number of pixels (columns)
+parameter.nv = 3000;    % number of pixels (rows)
+
+% Single voxel real size (mm)
+parameter.dx = .085; 
+parameter.dy = .085;
+parameter.dz = .1;
+
+% Single detector real size (mm)
+parameter.du = .085;   
+parameter.dv = .085;
+
+% X-ray source and detector distances
+parameter.DSD = 650;                            % Distance from source to detector (mm)
+parameter.DSO = 575;                            % Distance from source to the top of object (mm)
+parameter.DDR = 50;                             % Distance from detector to pivot (mm)
+parameter.DSR = parameter.DSD - parameter.DDR;  % Distance from source to pivot (mm)
+parameter.DAG = 19.95;                          % Distance of Air Gap (mm)         
+
+% Detector and object full real sizes (mm)
+parameter.sx = parameter.nx.*parameter.dx;  
+parameter.sy = parameter.ny.*parameter.dy;  
+parameter.sz = (parameter.nz.*parameter.dz)+parameter.DAG;  
+parameter.su = parameter.nu.*parameter.du;	
+parameter.sv = parameter.nv.*parameter.dv;  
+
+% Detector and object Volume grid settings 
+parameter.xs = (parameter.nx-1:-1:0)*parameter.dx;
+parameter.ys = (-(parameter.ny-1)/2:1:(parameter.ny-1)/2)*parameter.dy;
+parameter.zs = (0:1:parameter.nz-1)*parameter.dz + parameter.DAG;
+parameter.us = (parameter.nu-1:-1:0)*parameter.du;
+parameter.vs = (-(parameter.nv-1)/2:1:(parameter.nv-1)/2)*parameter.dv;
+
+% Number of Projections
+parameter.nProj = 25;
+
+% Noise parameters
+    % Poisson noise - number of photons
+    parameter.alpha = 1312.3;
+    parameter.It = 5.1;
+    parameter.Io = parameter.alpha * parameter.It;
+
+    % Secondary quantum noise - number of electrons
+    parameter.eo = 4.1226;
+
+    % Gaussian noise
+    parameter.mu = 401.8648;
+    parameter.sigma = 0.121;
+
+% Angle settings (Degrees)
+parameter.tubeAngle = 50;   % Tube Angle
+parameter.tubeDeg = linspace(parameter.tubeAngle/2,-parameter.tubeAngle/2,parameter.nProj);
+
+parameter.detAngle = 0;   % Detector Angle
+parameter.detectorDeg = linspace(-parameter.detAngle/2,parameter.detAngle/2,parameter.nProj);
+
+%% General parameters
+
+% Slice range to be saved
+parameter.sliceRange = 1:parameter.nz;
+
+% Region of interest (ROI) to store
+parameter.iROI = 1:parameter.ny;   
+parameter.jROI = 1:parameter.nx;    
+
+% Bit number quatization
+parameter.bitDepth = 16;
+ 

+ 106 - 0
Parameters/ParameterSettings_Phantom.m

@@ -0,0 +1,106 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: April, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% 
+%     DESCRIPTION:
+%     This is the configuration file for the tomosynthesis acquisition 
+%     geometry. This geometry is specified for the virtual phantom.
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. 
+%  
+%}
+% =========================================================================
+%% Physical Parameters Settings
+
+parameter.type = 'shepplogan';
+
+% Breast voxels density 
+parameter.nx = 1421;    % number of voxels (columns)
+parameter.ny = 1421;    % number of voxels (rows)
+parameter.nz = 500;     % number of voxels (slices)
+
+% Detector panel pixel density
+parameter.nu = 1500;    % number of pixels (columns)
+parameter.nv = 3000;    % number of pixels (rows)
+
+% Single voxel real size (mm)
+parameter.dx = .085; 
+parameter.dy = .085;
+parameter.dz = .1;
+
+% Single detector real size (mm)
+parameter.du = .085;   
+parameter.dv = .085;
+
+% X-ray source and detector distances
+parameter.DSD = 650;                                % Distance from source to detector (mm)
+parameter.DSO = 575;                                % Distance from source to the top of object (mm)
+parameter.DDR = 50;                                 % Distance from detector to pivot (mm)
+parameter.DSR = parameter.DSD - parameter.DDR;      % Distance from source to pivot (mm)
+parameter.DAG = 19.95;                              % Distance of Air Gap (mm)         
+
+% Detector and object full real sizes (mm)
+parameter.sx = parameter.nx.*parameter.dx;  
+parameter.sy = parameter.ny.*parameter.dy;  
+parameter.sz = (parameter.nz.*parameter.dz)+parameter.DAG;  
+parameter.su = parameter.nu.*parameter.du;	
+parameter.sv = parameter.nv.*parameter.dv;  
+
+% Detector and object Volume grid settings 
+parameter.xs = (parameter.nx-1:-1:0)*parameter.dx;
+parameter.ys = (-(parameter.ny-1)/2:1:(parameter.ny-1)/2)*parameter.dy;
+parameter.zs = (0:1:parameter.nz-1)*parameter.dz + parameter.DAG;
+parameter.us = (parameter.nu-1:-1:0)*parameter.du;
+parameter.vs = (-(parameter.nv-1)/2:1:(parameter.nv-1)/2)*parameter.dv;
+
+% Number of Projections
+parameter.nProj = 25;
+
+% Noise parameters
+    % Poisson noise - number of photons
+    parameter.alpha = 1312.3;
+    parameter.It = 5.1;
+    parameter.Io = parameter.alpha * parameter.It;
+
+    % Secondary quantum noise - number of electrons
+    parameter.eo = 4.1226;
+
+    % Gaussian noise
+    parameter.mu = 401.8648;
+    parameter.sigma = 0.121;
+
+% Angle settings (Degrees)
+parameter.tubeAngle = 50;   % Tube Angle
+parameter.tubeDeg = linspace(parameter.tubeAngle/2,-parameter.tubeAngle/2,parameter.nProj);
+
+parameter.detAngle = 0;   % Detector Angle
+parameter.detectorDeg = linspace(-parameter.detAngle/2,parameter.detAngle/2,parameter.nProj);
+
+%% General parameters
+
+% Slice range to be saved
+parameter.sliceRange = 1:parameter.nz;
+
+% Region of interest (ROI) to store
+parameter.iROI = 1:parameter.ny;   
+parameter.jROI = 1:parameter.nx;    
+
+% Bit number quatization
+parameter.bitDepth = 16;
+ 

+ 75 - 0
ProjectionNoising.m

@@ -0,0 +1,75 @@
+%% Author: Aleks Prša
+% Date: July, 2022
+% alex.prsa@gmail.com
+% =========================================================================
+%{
+% 
+%     DESCRIPTION:
+% 
+%     Program for noising projection images.
+%
+%     Faculty of Mathematic and Physics
+%     University in Ljubljana
+%     Ljubljana, Slovenia
+%
+%}
+% =========================================================================
+%%                      Choose initial number of photons
+
+close all;clear;clc
+
+It = 3;                  % Anode current - number of photon
+
+%% Load data
+
+path_User = userpath;
+path_ProjData = uigetdir(path_User);
+
+addpath(genpath('Functions'));
+addpath(genpath('Parameters'));
+ParameterSettings_MicroInsertion
+
+% Load projection data  
+if(path_ProjData == 0)
+    fprintf('Cancelled by user \n');
+	return;
+else
+    userpath(path_ProjData);
+
+    fprintf('Reading DICOMs   ')
+    answer = 'Noised';
+    [dataProj,infoDicom] = readDicom(path_ProjData,parameter,answer);
+    parameter.bitDepth = infoDicom(:,1).BitDepth;
+end
+
+%% Projection noising
+
+fprintf('\nProjection noising       ')
+for i=1:size(dataProj,3)
+    fprintf('\b\b\b\b\b%2d/%2d', i, size(dataProj,3))                           
+
+    attenuation(:,:,i) = (dataProj(:,:,i) - parameter.mu) / (parameter.eo*parameter.Io);
+    attenuation(attenuation < 0) = 0;
+    attenuation(attenuation > 1) = 1;
+
+    lambda = attenuation(:,:,i) .* (parameter.alpha * It);
+    newProjection(:,:,i) = parameter.eo * poissrnd(lambda,parameter.nv,parameter.nu) + parameter.mu;
+end
+
+%% Save data
+
+filestring = ['Output',filesep,'Noised',filesep,'Projections'];   
+    if(~exist(filestring))
+        mkdir(filestring)
+    end
+    
+fprintf('\nWriting DICOM projections   ')
+writeDicomProj(newProjection,infoDicom,filestring,parameter);
+
+fprintf('\nSaving projections... \n')
+save(['Output',filesep,'Noised',filesep,'NoisedProjections.mat'],'dataProj','-v7.3');
+infoDicom = [];
+
+fprintf('\nFINISHED - Data stored in "Noised" folder\n')
+
+beep

ファイルの差分が大きいため隠しています
+ 9 - 0
README.md


+ 290 - 0
Reconstruction.m

@@ -0,0 +1,290 @@
+%% Author: Rodrigo de Barros Vimieiro and Aleks Prša
+% Date: April, 2018; 2022
+% rodrigo.vimieiro@gmail.com / alex.prsa@gmail.com
+% =========================================================================
+%{
+% 
+%     DESCRIPTION:
+% 
+%     The goal of this software is to present an open source reconstruction 
+%     toolbox, which features the four basic types of DBT reconstruction, 
+%     with the possibility of different acquisition geometries. This 
+%     toolbox is intended for academic usage. Since it is an open source 
+%     toolbox, researchers are welcome to contribute to the current version 
+%     of the software.
+% 
+%     Department of Electrical and Computer Engineering, 
+%     São Carlos School of Engineering, 
+%     University of São Paulo, 
+%     São Carlos, Brazil.
+%
+%     Faculty of Mathematic and Physics
+%     University in Ljubljana
+%     Ljubljana, Slovenia
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.    
+%  
+%}
+% =========================================================================
+%%                      Reconstruction Code                              %%
+
+close all;clear;clc
+
+%% Global parameters
+
+global showinfo saveinfo animation 
+
+showinfo = uint8(1);        % Show projection animation
+saveinfo = uint8(1);        % Save reconstructed volume
+animation = uint8(1);       % Graphical animation
+
+%% GUI - Data decision
+
+answer = questdlg(['Load Images or Create a Virtual Phantom?'], ...
+    'Data selection', ...
+    'Images', 'Virtual', 'Images');
+
+% Handle response
+switch answer
+    case 'Virtual'
+        fprintf('Creating a Virtual Phantom... \n')
+        data = 2;
+    case 'Images'
+        answer = questdlg(['Load Clinical images, Microcalcification images or Noised images?'], ...
+            'Image selection', ...
+            'Clinical', 'Noised', 'Microcalcifications', 'Microcalcifications');
+        switch answer
+            case 'Clinical'
+                fprintf('Loading Clinical images... \n')
+                data = 1;
+            case 'Noised'
+                fprintf('Loading Noised images... \n')
+                data = 1; 
+            case 'Microcalcifications'
+                fprintf('Loading Microcalcifications images... \n')
+                data = 1;
+            otherwise
+                fprintf('Cancelled by user \n');
+    	        return;
+        end
+end
+
+%% Load components
+
+addpath(genpath('Functions'));
+addpath(genpath('Parameters'));
+
+if(~exist('Output','dir'))
+    mkdir('Output')
+    saveinfo = 1;
+end
+
+addpath('Output');
+
+if(data == 1)   %   ** Dicom data **
+    
+    ParameterSettings_GE
+
+    if(~exist(['Output',filesep,'Clinical'],'dir'))
+        mkdir(['Output',filesep,'Clinical'])
+    end
+           
+    % Load projection data    
+    path_User = userpath;
+    path_ProjData = uigetdir(path_User); 
+    
+    if(path_ProjData == 0)
+        fprintf('Cancelled by user \n');
+    	return;
+    else
+        userpath(path_ProjData)       
+        
+        fprintf('Reading DICOMs   ')
+        [dataProj,infoDicom] = readDicom(path_ProjData,parameter,answer);
+        parameter.bitDepth = infoDicom(:,1).BitDepth;
+    
+        % Pre-process projections
+        fprintf('\nPre-processing DICOMs... \n')
+        [dataProj,parameter] = dataPreprocess(dataProj,parameter);
+        dataProj = flip(dataProj, 2);                               % Source is on the left side
+
+        % Transform intensity image in attenuation coefficients
+        dataProj(dataProj == 0) = 1;
+        dataProj = -log(dataProj./(2^16-1));
+        
+        if(numel(find(dataProj==inf)) ~= 0)
+            error('Numeric error. Please, double-check if you input data has zeros on it')
+        end
+
+    end
+    
+else    %   ** Virtual data **
+    
+    ParameterSettings_Phantom;
+    
+    if(~exist(['Output',filesep,'Virtual'],'dir'))
+        mkdir(['Output',filesep,'Virtual'])
+    end
+    addpath(['Output',filesep,'Virtual']);
+
+    % Create virtual phantom
+    data3d = single(phantom(parameter));  
+    data3d(data3d<0) = eps;
+
+    data3d = flip(data3d, 1);                               % Phantom in accordance with coordinate system
+    data3d = flip(data3d, 2);                               % Source is on the left side
+
+    % Make the projections
+    if(animation || saveinfo)
+
+        % Tomosynthesis
+        figureScreenSize()
+        fprintf('Creating projections   ')
+        dataProj = projection(data3d,parameter,[]);
+        dataProj = flip(dataProj, 2);                       % Source is on the left side
+
+        % Save phantom projection as DICOM
+        filestring = ['Output',filesep,'Virtual',filesep,'Projections'];   
+            if(~exist(filestring))
+                mkdir(filestring)
+            end
+        fprintf('\nWriting DICOM projections   ')
+        writeDcmProj(dataProj,filestring,parameter);
+
+        if(saveinfo)
+            fprintf('\nSaving projections... \n')
+            save(['Output',filesep,'Virtual',filesep,'Projections.mat'],'dataProj','-v7.3')
+            infoDicom = [];
+        else
+            load Projections.mat
+        end
+    else
+        load Projections.mat
+    end
+
+    % Transform intensity image in attenuation coefficients
+    dataProj(dataProj == 0) = 0.00001;
+    dataProj = -log(dataProj./(2^16-1));
+    dataProj = flip(dataProj, 2);                           % Source is on the left side
+       
+    if(numel(find(dataProj==inf)) ~= 0)
+        error('Numeric error. Please, double-check if you input data has zeros on it')
+    end
+
+end
+
+fprintf('Starting reconstruction');
+
+%% Set specific recon parameters
+
+nIter = [2,3,4,8];           % Iteration to be saved (MLEM or SART)
+filterType = 'FBP';          % Filter type: 'BP', 'FBP'
+cutoff = 0.7;                % Percentage until cut off frequency (FBP)
+filterOrder = 4;             % Butterworth filter order
+
+%% Reconstruction methods
+
+%                       ## Uncomment to use ##
+dataRecon3d = FBP(dataProj,filterType,cutoff,filterOrder,parameter);
+dataProj = flip(dataProj, 2);
+dataRecon3d{1,1} = flip(dataRecon3d{1,1}, 2);
+if(saveinfo)
+    saveData(dataRecon3d,parameter,answer,[],infoDicom);
+end
+
+%                       ## Uncomment to use ##
+% dataRecon3d = MLEM(dataProj,nIter,parameter);
+% if(saveinfo)
+%     saveData(dataRecon3d,parameter,answer,[],infoDicom);
+% end
+
+%                       ## Uncomment to use ##
+% dataRecon3d = SART(dataProj,nIter,parameter);
+% if(saveinfo)
+%     saveData(dataRecon3d,parameter,answer,[],infoDicom);
+% end
+
+%                       ## Uncomment to use ##
+% dataRecon3d = SIRT(dataProj,nIter,parameter);
+% if(saveinfo)
+%     saveData(dataRecon3d,parameter,answer,[],infoDicom);
+% end
+
+fprintf('\n\nFINISHED - Data stored in "Output" folder\n')
+
+%% Show info
+
+% DICOM images
+if(animation && data == 1)
+%{
+    % Projected planes viewer
+    figure("Name", 'Projections', NumberTitle='off')
+    sliceViewer(dataProj);
+    %figure("Name", 'Projections', NumberTitle='off')
+    %montage(dataProj, 'Size', [5 5], 'DisplayRange', []);
+%
+    % Reconstructed slice viewer
+    figure("Name", 'Reconstructed images', NumberTitle='off')
+    sliceViewer(dataRecon3d{1,1});
+    %figure("Name", 'Reconstructed images', NumberTitle='off')
+    %montage(dataRecon3d{1,1}, 'Size', [10 round(parameter.nz/10)], 'DisplayRange', []);
+%}
+end
+
+% Virtual Phantom
+if(animation && data == 2)
+%
+    % Phantom slice viewer
+    figure("Name", 'Phantom', NumberTitle='off')
+    sliceViewer(data3d);
+    %sliceViewer(imrotate(data3d, 90));
+    %figure("Name", 'Phantom', NumberTitle='off')
+    %orthosliceViewer(imrotate(data3d, 90));
+    %figure("Name", 'Phantom', NumberTitle='off')
+    %montage(imrotate(data3d, 90), 'Size', [10 parameter.nz/10], 'DisplayRange', []);
+%
+    % Projected planes viewer
+    figure("Name", 'Projections', NumberTitle='off')
+    sliceViewer(dataProj);
+    %sliceViewer(imrotate(dataProj, 90));
+    %figure("Name", 'Projections', NumberTitle='off')
+    %montage(imrotate(dataProj, 90), 'Size', [3 3], 'DisplayRange', []);
+%
+    % Reconstructed slice viewer
+    figure("Name", 'Reconstructed phantom', NumberTitle='off')
+    sliceViewer(dataRecon3d{1,1});
+    %sliceViewer(imrotate(dataRecon3d{1,1}, 90));
+    %figure("Name", 'Phantom', NumberTitle='off')
+    %orthosliceViewer(imrotate(dataRecon3d{1,1}, 90));
+    %figure("Name", 'Reconstructed phantom', NumberTitle='off')
+    %montage(imrotate(dataRecon3d{1,1}, 90), 'Size', [10 parameter.nz/10], 'DisplayRange', []);
+%{
+    % 3D Backprojection Visualization
+    figureScreenSize()
+    for k=1:parameter.nz
+        subplot(1,2,1)
+        imshow(data3d(:,:,k))
+        title(['Phantom slice ',num2str(k)]);axis on;
+        subplot(1,2,2)
+        imshow(dataRecon3d{1,end}(:,:,k),[])   
+        title(['Reconstructed slice ',num2str(k)]);axis on;
+        pause(0.02)
+    end
+%}
+end
+
+beep

+ 118 - 0
SART.m

@@ -0,0 +1,118 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: May, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                       SART(proj,nIter,parameter)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function reconstruct iteratively the volume through 
+%     Simultaneous Algebraic Reconstruction Technique (SART) method.
+%     
+%     The geometry is for DBT with half cone-beam. All parameters are set in 
+%     "ParameterSettings" code. 
+%  
+%     INPUT:
+% 
+%     - proj  = 2D projection images 
+%     - nIter = Specific iterations to save volume data 
+%     - parameter = Parameter of all geometry
+% 
+%     OUTPUT:
+% 
+%     - allreconData3d{1,...} = Cell with Volumes of each specific iteration.
+%     - allreconData3d{2,...} = Cell with informations of each specific iteration.
+% 
+%     Reference: Three-Dimensional Digital Tomosynthesis - Yulia Levakhina (2014)
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% Recon Code -  Iterative reconstruction: SART
+function allreconData3d = SART(proj,nIter,parameter)
+
+global showinfo animation
+
+numProjs = parameter.nProj;
+
+info.startDateAndTime = char(datetime('now','Format','MM-dd-yyyy''  ''HH:mm:ss'));
+info.reconMeth = 'SART';
+
+highestValue = (2^parameter.bitDepth) - 1;
+
+allIterTime = zeros(nIter(end),1);   % Time data for each iteration
+allreconData3d = cell(1,size(nIter,2)); % Recon data for each iteration
+
+% Initial estimated data 
+reconData3d = zeros(parameter.ny, parameter.nx, parameter.nz,'single');
+
+% Pre calculation of Projection normalization
+tempvar = animation; animation = 0; 
+proj_norm = projection(ones(parameter.ny, parameter.nx, parameter.nz, 'single'),parameter, []);
+animation = tempvar; clear tempvar;
+
+% Pre calculation of Backprojection normalization
+vol_norm = backprojection(ones(parameter.nv, parameter.nu, parameter.nProj, 'single'), parameter, []);
+
+if(showinfo)
+    fprintf('----------------\nStarting SART Iterations... \n\n')
+end
+
+% Start Iterations
+for iter = 1:nIter(end)
+    tStart = tic;
+    % For each projection
+    for p=1:numProjs
+        
+        % Error between raw data and projection of estimated data 
+        proj_diff = proj(:,:,p) - projection(reconData3d,parameter,p);  
+
+        proj_diff = proj_diff ./ proj_norm(:,:,p); % Projection normalization
+        proj_diff(isnan(proj_diff)) = 0;
+        proj_diff(isinf(proj_diff)) = 0;
+
+        upt_term = backprojection(proj_diff,parameter,p);
+        upt_term = upt_term ./ vol_norm; % Volume normalization
+        upt_term(isnan(upt_term)) = 0;
+        upt_term(isinf(upt_term)) = 0;
+
+        % Updates the previous estimation 
+        reconData3d = reconData3d + upt_term; 
+    end
+        
+    % Truncate to highest and minimum value
+    reconData3d(reconData3d>highestValue) = highestValue;
+    reconData3d(reconData3d<0) = 0;
+
+    allIterTime(iter,1) = toc(tStart);
+    
+    % Save data 
+    indIter = find(nIter == iter);
+    if(indIter~=0)
+        allreconData3d{1,indIter} = reconData3d(parameter.iROI,parameter.jROI,parameter.sliceRange);
+        info.IterationNumber = num2str(iter);
+        info.IterationTime = num2str(sum(allIterTime(:)));
+        allreconData3d{2,indIter} = info;      
+    end
+    
+    if(showinfo) 
+        fprintf('Iteration %d Time: %.2f\n\n',iter,allIterTime(iter,1)); 
+    end
+    
+end
+end

+ 111 - 0
SIRT.m

@@ -0,0 +1,111 @@
+%% Author: Rodrigo de Barros Vimieiro
+% Date: Dec, 2018
+% rodrigo.vimieiro@gmail.com
+% =========================================================================
+%{
+% -------------------------------------------------------------------------
+%                       SIRT(proj,nIter,parameter)
+% -------------------------------------------------------------------------
+%     DESCRIPTION:
+%     This function reconstruct iteratively the volume through 
+%     Simultaneous Iterative Reconstruction Technique (SIRT) method.
+%     
+%     The geometry is for DBT with half cone-beam. All parameters are set in 
+%     "ParameterSettings" code. 
+%  
+%     INPUT:
+% 
+%     - proj  = 2D projection images 
+%     - nIter = Specific iterations to save volume data 
+%     - parameter = Parameter of all geometry
+% 
+%     OUTPUT:
+% 
+%     - allreconData3d{1,...} = Cell with Volumes of each specific iteration.
+%     - allreconData3d{2,...} = Cell with informations of each specific iteration.
+% 
+%     Reference: Three-Dimensional Digital Tomosynthesis - Yulia Levakhina (2014)
+% 
+%     ---------------------------------------------------------------------
+%     Copyright (C) <2018>  <Rodrigo de Barros Vimieiro>
+% 
+%     This program 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.
+% 
+%     This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+%}
+% =========================================================================
+%% Recon Code -  Iterative reconstruction: SIRT
+function allreconData3d = SIRT(proj,nIter,parameter)
+
+global showinfo animation
+
+info.startDateAndTime = char(datetime('now','Format','MM-dd-yyyy''  ''HH:mm:ss'));
+info.reconMeth = 'SIRT';
+
+highestValue = (2^parameter.bitDepth) - 1;
+
+allIterTime = zeros(nIter(end),1);   % Time data for each iteration
+allreconData3d = cell(1,size(nIter,2)); % Recon data for each iteration
+
+% Initial estimated data 
+reconData3d = zeros(parameter.ny, parameter.nx, parameter.nz,'single');
+
+% Pre calculation of Projection normalization
+tempvar = animation; animation = 0; 
+proj_norm = projection(ones(parameter.ny, parameter.nx, parameter.nz, 'single'),parameter,[]);
+animation = tempvar; clear tempvar;
+
+% Pre calculation of Backprojection normalization
+vol_norm = backprojection(ones(parameter.nv, parameter.nu, parameter.nProj, 'single'), parameter,[]);
+
+if(showinfo)
+    fprintf('----------------\nStarting SIRT Iterations... \n\n')
+end
+
+% Start Iterations
+for iter = 1:nIter(end)
+    tStart = tic;
+    
+    proj_diff = proj - projection(reconData3d,parameter,[]); % Error between raw data and projection of estimated data  
+    
+    proj_diff = proj_diff ./ proj_norm; % Projection normalization
+    proj_diff(isnan(proj_diff)) = 0;
+    proj_diff(isinf(proj_diff)) = 0;
+    
+    upt_term = backprojection(proj_diff, parameter,[]);
+    upt_term = upt_term ./ vol_norm; % Volume normalization
+    upt_term(isnan(upt_term)) = 0;
+    upt_term(isinf(upt_term)) = 0;
+
+    reconData3d = reconData3d + upt_term; % Updates the previous estimation 
+        
+    % Truncate to highest and minimum value
+    reconData3d(reconData3d>highestValue) = highestValue;
+    reconData3d(reconData3d<0) = 0;
+
+    allIterTime(iter,1) = toc(tStart);
+    
+    % Save data 
+    indIter = find(nIter == iter);
+    if(indIter~=0)
+        allreconData3d{1,indIter} = reconData3d(parameter.iROI,parameter.jROI,parameter.sliceRange);
+        info.IterationNumber = num2str(iter);
+        info.IterationTime = num2str(sum(allIterTime(:)));
+        allreconData3d{2,indIter} = info;      
+    end
+    
+    if(showinfo) 
+        fprintf('Itaration %d Time: %.2f\n\n',iter,allIterTime(iter,1)); 
+    end
+    
+end
+end

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません