projectionDDb_mex.cpp 18 KB

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