backprojectionDDb_mex.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. /*
  2. %% Author: Rodrigo de Barros Vimieiro
  3. % Date: March, 2019
  4. % rodrigo.vimieiro@gmail.com
  5. % =========================================================================
  6. %{
  7. % -------------------------------------------------------------------------
  8. % backprojectionDDb_mex(proj,param)
  9. % -------------------------------------------------------------------------
  10. % DESCRIPTION:
  11. % This function reconstruct the 3D volume from projections, based on
  12. % the Distance-Driven principle. It works by calculating the overlap
  13. % in X and Y axis of the volume and the detector boundaries.
  14. % The geometry is for DBT with half cone-beam. All parameters are set
  15. % in "ParameterSettings" code.
  16. %
  17. % INPUT:
  18. %
  19. % - proj = 2D projections for each angle
  20. % - param = Parameter of all geometry
  21. %
  22. % OUTPUT:
  23. %
  24. % - data3d = reconstructed volume.
  25. %
  26. % Reference:
  27. % - Branchless Distance Driven Projection and Backprojection,
  28. % Samit Basu and Bruno De Man (2006)
  29. % - GPU Acceleration of Branchless Distance Driven Projection and
  30. % Backprojection, Liu et al (2016)
  31. % - GPU-Based Branchless Distance-Driven Projection and Backprojection,
  32. % Liu et al (2017)
  33. % - A GPU Implementation of Distance-Driven Computed Tomography,
  34. % Ryan D. Wagner (2017)
  35. % ---------------------------------------------------------------------
  36. % Copyright (C) <2019> <Rodrigo de Barros Vimieiro>
  37. %
  38. % This program is free software: you can redistribute it and/or modify
  39. % it under the terms of the GNU General Public License as published by
  40. % the Free Software Foundation, either version 3 of the License, or
  41. % (at your option) any later version.
  42. %
  43. % This program is distributed in the hope that it will be useful,
  44. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  45. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  46. % GNU General Public License for more details.
  47. %
  48. % You should have received a copy of the GNU General Public License
  49. % along with this program. If not, see <http://www.gnu.org/licenses/>.
  50. %}
  51. % =========================================================================
  52. %% 3-D Back-projection Branchless Distance Driven Code (CPU-Multithread)
  53. */
  54. #include "backprojectionDDb_mex.h"
  55. /****************************************************************************
  56. * function: mexFunction() - Matlab interface function. *
  57. * INPUTS: *
  58. * nlhs - Number of expected output mxArrays, specified as an integer. *
  59. * plhs[] - Array of pointers to the expected mxArray output arguments. *
  60. * nrhs - Number of input mxArrays, specified as an integer. *
  61. * prhs[] - Array of pointers to the mxArray input arguments. *
  62. ****************************************************************************/
  63. void mexFunction(int nlhs, mxArray *plhs[],
  64. int nrhs, const mxArray *prhs[]) {
  65. /* Check for proper number of arguments */
  66. if (nrhs != 2) {
  67. mexErrMsgIdAndTxt("MATLAB:mexcpp:nargin",
  68. "backprojection_mex requires two input arguments.");
  69. }
  70. if (nlhs != 1) {
  71. mexErrMsgIdAndTxt("MATLAB:mexcpp:nargout",
  72. "backprojection_mex requires one output argument.");
  73. }
  74. /* Check if the input is of proper type */
  75. if (!mxIsDouble(prhs[0])) {
  76. mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
  77. "First argument has to be double.");
  78. }
  79. if (!mxIsStruct(prhs[1])) {
  80. mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
  81. "Second argument has to be a struct.");
  82. }
  83. double* const pProj = (double*)(mxGetPr(prhs[0])); // This variable has to come as single/double
  84. double* const pTubeAngleD = mxGetPr(mxGetField(prhs[1], 0, "tubeDeg"));
  85. double* const pDetAngleD = mxGetPr(mxGetField(prhs[1], 0, "detectorDeg"));
  86. const int unsigned nProj = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nProj"));
  87. const int unsigned nPixX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nx"));
  88. const int unsigned nPixY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "ny"));
  89. const int unsigned nSlices = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nz"));
  90. const int unsigned nDetX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nu"));
  91. const int unsigned nDetY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nv"));
  92. const double dx = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dx"));
  93. const double dy = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dy"));
  94. const double dz = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dz"));
  95. const double du = (const double)mxGetScalar(mxGetField(prhs[1], 0, "du"));
  96. const double dv = (const double)mxGetScalar(mxGetField(prhs[1], 0, "dv"));
  97. const double DSD = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DSD"));
  98. const double DDR = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DDR"));
  99. const double DAG = (const double)mxGetScalar(mxGetField(prhs[1], 0, "DAG"));
  100. mwSize NRow = mxGetM(prhs[0]);
  101. mwSize NCol = mxGetN(prhs[0]);
  102. mwSize NElemVol = mxGetNumberOfElements(prhs[0]);
  103. /* Check if the input is in proper size */
  104. if (NRow != nDetY) {
  105. mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
  106. "First argument needs to have the same number of rows as in the configuration file.");
  107. }
  108. if (NCol / nProj != nDetX) {
  109. mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
  110. "First argument needs to have the same number of columns as in the configuration file.");
  111. }
  112. // Cast single vectors to double
  113. double* const pTubeAngle = (double*)mxMalloc(nProj * sizeof(double));
  114. double* const pDetAngle = (double*)mxMalloc(nProj * sizeof(double));
  115. for (unsigned int n = 0; n < nProj; n++) {
  116. pTubeAngle[n] = (double)pTubeAngleD[n];
  117. pDetAngle[n] = (double)pDetAngleD[n];
  118. }
  119. //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);
  120. // Output memory allocation
  121. mwSize dims[3] = { nPixY,nPixX,nSlices };
  122. plhs[0] = mxCreateNumericArray(3, dims, mxDOUBLE_CLASS, mxREAL);
  123. double* const pVolume = (double*)mxGetData(plhs[0]);
  124. backprojectionDDb(pVolume, pProj, pTubeAngle, pDetAngle, nProj, nPixX, nPixY, nSlices, nDetX, nDetY, dx, dy, dz, du, dv, DSD, DDR, DAG);
  125. mxFree(pTubeAngle);
  126. mxFree(pDetAngle);
  127. }
  128. // CPU Branchless Distance-driven back-projection
  129. void backprojectionDDb(double* const pVolume,
  130. double* const pProj,
  131. double* const pTubeAngle,
  132. double* const pDetAngle,
  133. const int nProj,
  134. const int nPixX,
  135. const int nPixY,
  136. const int nSlices,
  137. const int nDetX,
  138. const int nDetY,
  139. const double dx,
  140. const double dy,
  141. const double dz,
  142. const double du,
  143. const double dv,
  144. const double DSD,
  145. const double DDR,
  146. const double DAG)
  147. {
  148. nThreads = omp_get_max_threads();
  149. mexPrintf("CPU running with maximum number of threads: %d \n", nThreads);
  150. clock_t time;
  151. time = clock();
  152. // Number of mapped detector and pixels
  153. const int nDetXMap = nDetX + 1;
  154. const int nDetYMap = nDetY + 1;
  155. const int nPixXMap = nPixX + 1;
  156. const int nPixYMap = nPixY + 1;
  157. // Allocate memory
  158. double* const pSliceI = (double*)mxMalloc(nPixXMap*nPixYMap * sizeof(double));
  159. // Pointer for projections coordinates
  160. // Allocate memory for projections coordinates
  161. double* const pDetX = (double*)mxMalloc(nDetXMap * sizeof(double));
  162. double* const pDetY = (double*)mxMalloc(nDetYMap * sizeof(double));
  163. double* const pDetZ = (double*)mxMalloc(nDetYMap * sizeof(double));
  164. double* const pObjX = (double*)mxMalloc(nPixXMap * sizeof(double));
  165. double* const pObjY = (double*)mxMalloc(nPixYMap * sizeof(double));
  166. double* const pObjZ = (double*)mxMalloc(nSlices * sizeof(double));
  167. // Pointer for mapped coordinates
  168. // Allocate memory for mapped coordinates
  169. double* const pDetmY = (double*)mxMalloc(nDetYMap * sizeof(double));
  170. double* const pDetmX = (double*)mxMalloc(nDetYMap * nDetXMap * sizeof(double));
  171. // Pointer for rotated detector coords
  172. // Allocate memory for rotated detector coords
  173. double* const pRdetY = (double*)mxMalloc(nDetYMap * sizeof(double));
  174. double* const pRdetZ = (double*)mxMalloc(nDetYMap * sizeof(double));
  175. // Map detector and object boudaries
  176. mapBoudaries(pDetX, nDetXMap, (double)nDetX, -du, 0.0);
  177. mapBoudaries(pDetY, nDetYMap, nDetY / 2.0, dv, 0.0);
  178. mapBoudaries(pDetZ, nDetYMap, 0.0, 0.0, 0.0);
  179. mapBoudaries(pObjX, nPixXMap, (double)nPixX, -dx, 0.0);
  180. mapBoudaries(pObjY, nPixYMap, nPixY / 2.0, dy, 0.0);
  181. mapBoudaries(pObjZ, nSlices, 0.0, dz, DAG + (dz / 2.0));
  182. // X - ray tube initial position
  183. double tubeX = 0;
  184. double tubeY = 0;
  185. double tubeZ = DSD;
  186. // Iso - center position
  187. double isoY = 0;
  188. double isoZ = DDR;
  189. // Allocate memory for temp projection variable
  190. double* const pProjt = (double*)mxMalloc(nDetYMap *nDetXMap * nProj * sizeof(double));
  191. double* const pVolumet = (double*)mxMalloc(nPixY *nPixX * nSlices * sizeof(double));
  192. // Initiate variables value with 0
  193. for (int nz = 0; nz < nSlices; nz++)
  194. for (int x = 0; x < nPixX; x++)
  195. for (int y = 0; y < nPixY; y++)
  196. pVolumet[(nz*nPixY *nPixX) + (x*nPixY) + y] = 0;
  197. // Integration of 2D slices over the whole volume
  198. // (S.1.Integration. - Liu et al(2017))
  199. /*
  200. 2 - D image
  201. -->J
  202. | -------------
  203. v | |
  204. I | |
  205. | |
  206. | |
  207. -------------
  208. */
  209. // Initialize first column and row with zeros
  210. for (int p = 0; p < nProj; p++) {
  211. for (int y = 0; y < nDetYMap; y++)
  212. pProjt[(p*nDetYMap *nDetXMap) + y] = 0;
  213. for (int x = 1; x < nDetXMap; x++)
  214. pProjt[(p*nDetYMap *nDetXMap) + (x*nDetYMap)] = 0;
  215. }
  216. // Integrate on I direction
  217. for (int p = 0; p < nProj; p++)
  218. #pragma omp parallel for num_threads(nThreads) schedule(static)
  219. for (int x = 0; x < nDetX; x++){
  220. double sum = 0;
  221. for (int y = 0; y < nDetY; y++){
  222. sum += pProj[(p*nDetY *nDetX) + (x*nDetY) + y];
  223. pProjt[(p*nDetYMap *nDetXMap) + ((x+1)*nDetYMap) + y + 1] = sum;
  224. }
  225. }
  226. // Integrate on J direction
  227. for (int p = 0; p < nProj; p++)
  228. #pragma omp parallel for num_threads(nThreads) schedule(static)
  229. for (int y = 1; y < nDetYMap; y++)
  230. for (int x = 2; x < nDetXMap; x++)
  231. pProjt[(p*nDetYMap *nDetXMap) + (x*nDetYMap) + y] += pProjt[(p*nDetYMap *nDetXMap) + ((x - 1)*nDetYMap) + y];
  232. double* pDetmX_tmp = pDetmX + (nDetYMap * (nDetXMap - 2));
  233. // For each projection
  234. for (int p = 0; p < nProj; p++) {
  235. // Get specif tube angle for the projection
  236. double theta = pTubeAngle[p] * M_PI / 180.0;
  237. // Get specif detector angle for the projection
  238. double phi = pDetAngle[p] * M_PI / 180.0;
  239. //mexPrintf("Tube angle:%f Det angle:%f\n", theta, phi);
  240. // Tube rotation
  241. double rtubeY = ((tubeY - isoY)*cos(theta) - (tubeZ - isoZ)*sin(theta)) + isoY;
  242. double rtubeZ = ((tubeY - isoY)*sin(theta) + (tubeZ - isoZ)*cos(theta)) + isoZ;
  243. //mexPrintf("R tube Y:%f R tube Z:%f\n", rtubeY, rtubeZ);
  244. // Detector rotation
  245. for (int y = 0; y < nDetYMap; y++) {
  246. pRdetY[y] = ((pDetY[y] - isoY)*cos(phi) - (pDetZ[y] - isoZ)*sin(phi)) + isoY;
  247. pRdetZ[y] = ((pDetY[y] - isoY)*sin(phi) + (pDetZ[y] - isoZ)*cos(phi)) + isoZ;
  248. }
  249. // For each slice
  250. for (int nz = 0; nz < nSlices; nz++) {
  251. /*
  252. Map detector onto XY plane(Inside proj loop in case detector rotates)
  253. *** Note: Matlab has linear indexing as a column of elements, i.e, the elements are actually stored in memory as queued columns.
  254. */
  255. // Map slice onto XY plane
  256. mapDet2Slice(pDetmX, pDetmY, tubeX, rtubeY, rtubeZ, pDetX, pRdetY, pRdetZ, pObjZ[nz], nDetXMap, nDetYMap);
  257. /*
  258. S.2. Interpolation - Liu et al (2017)
  259. */
  260. bilinear_interpolation(pSliceI, pProjt, pObjX, pObjY, pDetmX_tmp, pDetmY, nPixXMap, nPixYMap, nDetXMap, nDetYMap, nDetX, nDetY, p);
  261. /*
  262. S.3. Differentiation - Eq. 24 - Liu et al (2017)
  263. */
  264. differentiation(pVolumet, pSliceI, tubeX, rtubeY, rtubeZ, pObjX, pObjY, pObjZ, nPixX, nPixY, nPixXMap, nPixYMap, du, dv, dx, dy, dz, nz);
  265. } // Loop end slices
  266. } // Loop end Projections
  267. // Copy pProjt to pProj
  268. for (int nz = 0; nz < nSlices; nz++)
  269. for (int x = 0; x < nPixX; x++)
  270. for (int y = 0; y < nPixY; y++)
  271. pVolume[(nz*nPixX*nPixY) + (x*nPixY) + y] = pVolumet[(nz*nPixX*nPixY) + (x*nPixY) + y] / (double) nProj;
  272. // Interp
  273. //for (int x = 0; x < nDetXMap; x++)
  274. // for (int y = 0; y < nDetYMap; y++) {
  275. // pProj[(0 * nDetXMap*nDetYMap) + (x*nDetYMap) + y] = projI[(x*nDetYMap) + y];
  276. // }
  277. // Volume
  278. //for (int p = 0; p < nSlices; p++)
  279. // for (int x = 0; x < nPixXMap; x++)
  280. // for (int y = 0; y < nPixYMap+1; y++) {
  281. // pProj[(p * nPixXMap*nPixYMap) + (x*nPixYMap) + y] = pVolumet[(p * nPixXMap*nPixYMap) + (x*nPixYMap) + y];
  282. // }
  283. mxFree(pProjt);
  284. mxFree(pSliceI);
  285. mxFree(pVolumet);
  286. mxFree(pDetX);
  287. mxFree(pDetY);
  288. mxFree(pDetZ);
  289. mxFree(pObjX);
  290. mxFree(pObjY);
  291. mxFree(pObjZ);
  292. mxFree(pDetmY);
  293. mxFree(pDetmX);
  294. mxFree(pRdetY);
  295. mxFree(pRdetZ);
  296. time = clock() - time;
  297. mexPrintf("Processing time: %f seconds.\n", ((double)time) / CLOCKS_PER_SEC);
  298. return;
  299. }
  300. // Make boundaries of detector and slices
  301. void mapBoudaries(double* pBound,
  302. const int nElem,
  303. const double valueLeftBound,
  304. const double sizeElem,
  305. const double offset){
  306. for (int k = 0; k < nElem; k++)
  307. pBound[k] = (k - valueLeftBound) * sizeElem + offset;
  308. return;
  309. }
  310. // Map on XY plane
  311. void mapDet2Slice(double* const pXmapp,
  312. double* const pYmapp,
  313. double tubeX,
  314. double tubeY,
  315. double tubeZ,
  316. double * const pXcoord,
  317. double * const pYcoord,
  318. double * const pZcoord,
  319. double ZSlicecoord,
  320. const int nXelem,
  321. const int nYelem){
  322. #pragma omp parallel num_threads(nThreads)
  323. {
  324. int ind;
  325. #pragma omp for schedule(static)
  326. for (int x = 0; x < nXelem; x++)
  327. for (int y = 0; y < nYelem; y++) {
  328. ind = (x*nYelem) + y;
  329. pXmapp[ind] = ((pXcoord[x] - tubeX)*(ZSlicecoord - pZcoord[y]) - (pXcoord[x] * tubeZ) + (pXcoord[x] * pZcoord[y])) / (-tubeZ + pZcoord[y]);
  330. if (x == 0)
  331. pYmapp[y] = ((pYcoord[y] - tubeY)*(ZSlicecoord - pZcoord[y]) - (pYcoord[y] * tubeZ) + (pYcoord[y] * pZcoord[y])) / (-tubeZ + pZcoord[y]);
  332. }
  333. }
  334. return;
  335. }
  336. // Bilinear interpolation
  337. void bilinear_interpolation(double* pSliceI,
  338. double* pProj,
  339. double* pObjX,
  340. double* pObjY,
  341. double* pDetmX,
  342. double* pDetmY,
  343. const int nPixXMap,
  344. const int nPixYMap,
  345. const int nDetXMap,
  346. const int nDetYMap,
  347. const int nDetX,
  348. const int nDetY,
  349. const unsigned int np){
  350. /*
  351. S.2. Interpolation - Liu et al (2017)
  352. Reference:
  353. - https://en.wikipedia.org/wiki/Bilinear_interpolation
  354. - https://stackoverflow.com/questions/21128731/bilinear-interpolation-in-c-c-and-cuda
  355. Note: *** We are using the Unit Square equation ***
  356. alpha = X - X1
  357. 1-alpha = X2 - X
  358. beta = Y - Y1
  359. 1-beta = Y2 - Y
  360. ----> Xcoord
  361. | 0___________
  362. v |_d00_|_d10_|
  363. Ycoord |_d01_|_d11_|
  364. Matlab code --
  365. objX = nDetX - (objX ./ detmX(1, end - 1));
  366. objY = (objY ./ detmX(1, end - 1)) - (detmY(1) / detmX(1, end - 1));
  367. */
  368. #pragma omp parallel for num_threads(nThreads) schedule(static)
  369. for (int x = 0; x < nPixXMap; x++)
  370. for (int y = 0; y < nPixYMap; y++) {
  371. // Adjust the mapped coordinates to cross the range of (0-nDetX).*duMap
  372. // Divide by pixelSize to get a unitary pixel size
  373. const double xNormData = nDetX - pObjX[x] / pDetmX[0];
  374. const signed int xData = (signed int) floor(xNormData);
  375. const double alpha = xNormData - xData;
  376. // Adjust the mapped coordinates to cross the range of (0-nDetY).*dyMap
  377. // Divide by pixelSize to get a unitary pixel size
  378. const double yNormData = (pObjY[y] / pDetmX[0]) - (pDetmY[0] / pDetmX[0]);
  379. const signed int yData = (signed int) floor(yNormData);
  380. const double beta = yNormData - yData;
  381. double d00, d01, d10, d11;
  382. if (((xNormData) >= 0) && ((xNormData) <= nDetX) && ((yNormData) >= 0) && ((yNormData) <= nDetY)) d00 = pProj[(np*nDetYMap*nDetXMap) + (xData*nDetYMap + yData)]; else d00 = 0.0;
  383. if (((xData + 1) > 0) && ((xData + 1) <= nDetX) && ((yNormData) >= 0) && ((yNormData) <= nDetY)) d10 = pProj[(np*nDetYMap*nDetXMap) + ((xData + 1)*nDetYMap + yData)]; else d10 = 0.0;
  384. if (((xNormData) >= 0) && ((xNormData) <= nDetX) && ((yData + 1) > 0) && ((yData + 1) <= nDetY)) d01 = pProj[(np*nDetYMap*nDetXMap) + (xData*nDetYMap + yData + 1)]; else d01 = 0.0;
  385. 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;
  386. double result_temp1 = alpha * d10 + (-d00 * alpha + d00);
  387. double result_temp2 = alpha * d11 + (-d01 * alpha + d01);
  388. pSliceI[x * nPixYMap + y] = beta * result_temp2 + (-result_temp1 * beta + result_temp1);
  389. }
  390. return;
  391. }
  392. // Differentiation
  393. void differentiation(double* pVolume,
  394. double* pSliceI,
  395. double tubeX,
  396. double rtubeY,
  397. double rtubeZ,
  398. double* const pObjX,
  399. double* const pObjY,
  400. double* const pObjZ,
  401. const int nPixX,
  402. const int nPixY,
  403. const int nPixXMap,
  404. const int nPixYMap,
  405. const double du,
  406. const double dv,
  407. const double dx,
  408. const double dy,
  409. const double dz,
  410. const unsigned int nz){
  411. /*
  412. S.3. Differentiation - Eq. 24 - Liu et al (2017)
  413. Slice integral projection
  414. ___________
  415. |_A_|_B_|___|
  416. |_C_|_D_|___|
  417. |___|___|___|
  418. (y,x)
  419. ________________
  420. |_A_|__B__|_____|
  421. |_C_|(0,0)|(0,1)|
  422. |___|(1,0)|(1,1)|
  423. Coordinates on intergal slice:
  424. A = x * nPixYMap + y
  425. B = ((x+1) * nPixYMap) + y
  426. C = x * nPixYMap + y + 1
  427. D = ((x+1) * nPixYMap) + y + 1
  428. */
  429. #pragma omp parallel num_threads(nThreads)
  430. {
  431. unsigned int coordA;
  432. unsigned int coordB;
  433. unsigned int coordC;
  434. unsigned int coordD;
  435. double detMapSizeX;
  436. double detMapSizeY;
  437. double gamma;
  438. double alpha;
  439. double dA, dB, dC, dD;
  440. #pragma omp for schedule(static)
  441. for (int x = 0; x < nPixX; x++)
  442. for (int y = 0; y < nPixY; y++) {
  443. coordA = x * nPixYMap + y;
  444. coordB = ((x + 1) * nPixYMap) + y;
  445. coordC = coordA + 1;
  446. coordD = coordB + 1;
  447. // x - ray angle in X coord
  448. gamma = atan((pObjX[x] + (dx / 2) - tubeX) / (rtubeZ - pObjZ[nz]));
  449. // x - ray angle in Y coord
  450. alpha = atan((pObjY[y] + (dy / 2) - rtubeY) / (rtubeZ - pObjZ[nz]));
  451. dA = pSliceI[coordA];
  452. dB = pSliceI[coordB];
  453. dC = pSliceI[coordC];
  454. dD = pSliceI[coordD];
  455. // Treat border of interpolated integral detector
  456. if (dC == 0 && dD == 0) {
  457. dC = dA;
  458. dD = dB;
  459. }
  460. // S.3.Differentiation - Eq. 24 - Liu et al(2017)
  461. pVolume[(nPixX*nPixY*nz) + (x * nPixY) + y] += ((dD - dC - dB + dA)*(du*dv*dz / (cos(alpha)*cos(gamma)*dx*dy)));
  462. }
  463. }
  464. return;
  465. }