backprojectionDD_mex.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /*
  2. %% Author: Rodrigo de Barros Vimieiro
  3. % Date: September, 2018
  4. % rodrigo.vimieiro@gmail.com
  5. % =========================================================================
  6. %{
  7. % -------------------------------------------------------------------------
  8. % backprojectionDD_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: Three-Dimensional Digital Tomosynthesis - Yulia
  27. % Levakhina (2014), Cap 3.6 and 3.7.
  28. %
  29. % Original Paper: De Man, Bruno, and Samit Basu. "Distance-driven
  30. % projection and backprojection in three dimensions." Physics in
  31. % Medicine & Biology (2004).
  32. %
  33. % ---------------------------------------------------------------------
  34. % Copyright (C) <2018> <Rodrigo de Barros Vimieiro>
  35. %
  36. % This program is free software: you can redistribute it and/or modify
  37. % it under the terms of the GNU General Public License as published by
  38. % the Free Software Foundation, either version 3 of the License, or
  39. % (at your option) any later version.
  40. %
  41. % This program is distributed in the hope that it will be useful,
  42. % but WITHOUT ANY WARRANTY; without even the implied warranty of
  43. % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  44. % GNU General Public License for more details.
  45. %
  46. % You should have received a copy of the GNU General Public License
  47. % along with this program. If not, see <http://www.gnu.org/licenses/>.
  48. %}
  49. % =========================================================================
  50. %% 3-D Distance Driven Back-projection Code
  51. */
  52. #include "backprojectionDD_mex.h"
  53. /****************************************************************************
  54. * function: mexFunction() - Matlab interface function. *
  55. * INPUTS: *
  56. * nlhs - Number of expected output mxArrays, specified as an integer. *
  57. * plhs[] - Array of pointers to the expected mxArray output arguments. *
  58. * nrhs - Number of input mxArrays, specified as an integer. *
  59. * prhs[] - Array of pointers to the mxArray input arguments. *
  60. ****************************************************************************/
  61. void mexFunction(int nlhs, mxArray *plhs[],
  62. int nrhs, const mxArray *prhs[]) {
  63. /* Check for proper number of arguments */
  64. if (nrhs != 2) {
  65. mexErrMsgIdAndTxt("MATLAB:mexcpp:nargin",
  66. "projection_mex requires two input arguments.");
  67. }
  68. if (nlhs != 1) {
  69. mexErrMsgIdAndTxt("MATLAB:mexcpp:nargout",
  70. "projection_mex requires one output argument.");
  71. }
  72. /* Check if the input is of proper type */
  73. if (!mxIsSingle(prhs[0])) {
  74. mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
  75. "First argument has to be single.");
  76. }
  77. if (!mxIsStruct(prhs[1])) {
  78. mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
  79. "Second argument has to be a struct.");
  80. }
  81. float* pProj = (float*)mxGetPr(prhs[0]);
  82. double* pTubeAngleD = mxGetPr(mxGetField(prhs[1], 0, "tubeDeg"));
  83. double* pDetAngleD = mxGetPr(mxGetField(prhs[1], 0, "detectorDeg"));
  84. const int nProj = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nProj"));
  85. const int nPixX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nx"));
  86. const int nPixY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "ny"));
  87. const int nSlices = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nz"));
  88. const int nDetX = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nu"));
  89. const int nDetY = (const int)mxGetScalar(mxGetField(prhs[1], 0, "nv"));
  90. const float dx = (const float)mxGetScalar(mxGetField(prhs[1], 0, "dx"));
  91. const float dy = (const float)mxGetScalar(mxGetField(prhs[1], 0, "dy"));
  92. const float dz = (const float)mxGetScalar(mxGetField(prhs[1], 0, "dz"));
  93. const float du = (const float)mxGetScalar(mxGetField(prhs[1], 0, "du"));
  94. const float dv = (const float)mxGetScalar(mxGetField(prhs[1], 0, "dv"));
  95. const float DSD = (const float)mxGetScalar(mxGetField(prhs[1], 0, "DSD"));
  96. const float DDR = (const float)mxGetScalar(mxGetField(prhs[1], 0, "DDR"));
  97. const float DAG = (const float)mxGetScalar(mxGetField(prhs[1], 0, "DAG"));
  98. mwSize NRow = mxGetM(prhs[0]);
  99. mwSize NCol = mxGetN(prhs[0]);
  100. mwSize NElemVol = mxGetNumberOfElements(prhs[0]);
  101. /* Check if the input is in proper size */
  102. if (NRow != nDetY) {
  103. mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
  104. "First argument needs to have the same number of rows as in the configuration file.");
  105. }
  106. if (NCol / nProj != nDetX) {
  107. mexErrMsgIdAndTxt("MATLAB:mexcpp:typeargin",
  108. "First argument needs to have the same number of columns as in the configuration file.");
  109. }
  110. // Cast double vectors to single
  111. float* const pTubeAngle = (float*)mxMalloc(nProj * sizeof(float));
  112. float* const pDetAngle = (float*)mxMalloc(nProj * sizeof(float));
  113. for (int n = 0; n < nProj; n++) {
  114. pTubeAngle[n] = (float)pTubeAngleD[n];
  115. pDetAngle[n] = (float)pDetAngleD[n];
  116. }
  117. //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);
  118. const int nDetXMap = nDetX + 1;
  119. const int nDetYMap = nDetY + 1;
  120. const int nPixXMap = nPixX + 1;
  121. const int nPixYMap = nPixY + 1;
  122. // Memory allocation for projections coordinates
  123. float* const pDetX = (float*)mxMalloc(nDetXMap * sizeof(float));
  124. float* const pDetY = (float*)mxMalloc(nDetYMap * sizeof(float));
  125. float* const pDetZ = (float*)mxMalloc(nDetYMap * sizeof(float));
  126. float* const pObjX = (float*)mxMalloc(nPixXMap * sizeof(float));
  127. float* const pObjY = (float*)mxMalloc(nPixYMap * sizeof(float));
  128. float* const pObjZ = (float*)mxMalloc(nSlices * sizeof(float));
  129. // Memory allocation for mapped coordinates
  130. float* const pDetmY = (float*)mxMalloc(nDetYMap * sizeof(float));
  131. float* const pDetmX = (float*)mxMalloc(nDetYMap * nDetXMap * sizeof(float));
  132. float* const pPixmX = (float*)mxMalloc(nPixXMap * sizeof(float));
  133. float* const pPixmY = (float*)mxMalloc(nPixYMap * sizeof(float));
  134. // Memory allocation for rotated detecto coords
  135. float* const pRdetY = (float*)mxMalloc(nDetYMap * sizeof(float));
  136. float* const pRdetZ = (float*)mxMalloc(nDetYMap * sizeof(float));
  137. // Map detector and object boudaries
  138. for (int k = nDetX, ind = 0; k >= 0; k--, ind++) pDetX[ind] = k * du;
  139. for (int k = -nDetY / 2, ind = 0; k <= nDetY / 2; k++, ind++) pDetY[ind] = k * dv;
  140. for (int k = 0; k <= nDetY; k++) pDetZ[k] = 0;
  141. for (int k = nPixX, ind = 0; k >= 0; k--, ind++) pObjX[ind] = k * dx;
  142. for (int k = -nPixY / 2, ind = 0; k <= nPixY / 2; k++, ind++) pObjY[ind] = k * dy;
  143. for (int k = 0, ind = 0; k <= nSlices; k++, ind++) pObjZ[ind] = k * dz + DAG + dz / 2;
  144. // X - ray tube initial position
  145. float tubeX = 0;
  146. float tubeY = 0;
  147. float tubeZ = DSD;
  148. // Iso - center position
  149. float isoY = 0;
  150. float isoZ = DDR;
  151. // Allocate memory for temp projection variable
  152. float* const pVolumet = (float*)mxMalloc(nPixY *nPixX * nSlices * sizeof(float));
  153. // Initiate temp volume with zeros
  154. for (int z = 0; z < nSlices; z++)
  155. for (int x = 0; x < nPixX; x++)
  156. for (int y = 0; y < nPixY; y++)
  157. pVolumet[(z*nPixX*nPixY) + (x*nPixY) + y] = 0;
  158. // Allocate memory for flipped projection
  159. float* const pProjf = (float*)mxMalloc(nDetY *nDetX * nProj * sizeof(float));
  160. // Flip projection X (Img coord is reverse to Global)
  161. for (int p = 0; p < nProj; p++)
  162. for (int x = 0, x_inv = nDetX - 1; x < nDetX; x++, x_inv--)
  163. for (int y = 0; y < nDetY; y++)
  164. pProjf[(p*nDetX*nDetY) + (x*nDetY) + y] = pProj[(p*nDetX*nDetY) + (x_inv*nDetY) + y];
  165. // For each projection
  166. for (int p = 0; p < nProj; p++) {
  167. // Get specif tube angle for the projection
  168. float theta = pTubeAngle[p] * M_PI / 180.f;
  169. // Get specif detector angle for the projection
  170. float phi = pDetAngle[p] * M_PI / 180.f;
  171. // Tubre rotation
  172. float rtubeY = ((tubeY - isoY)*(float)cos(theta) - (tubeZ - isoZ)*(float)sin(theta)) + isoY;
  173. float rtubeZ = ((tubeY - isoY)*(float)sin(theta) + (tubeZ - isoZ)*(float)cos(theta)) + isoZ;
  174. // Detector rotation
  175. for (int v = 0; v < nDetYMap; v++) {
  176. pRdetY[v] = ((pDetY[v] - isoY)*(float)cos(phi) - (pDetZ[v] - isoZ)*(float)sin(phi)) + isoY;
  177. pRdetZ[v] = ((pDetY[v] - isoY)*(float)sin(phi) + (pDetZ[v] - isoZ)*(float)cos(phi)) + isoZ;
  178. }
  179. // Map detector onto XY plane(Inside proj loop in case detector rotates)
  180. mapp2xy(pDetmX, pDetmY, tubeX, rtubeY, rtubeZ, pDetX, pRdetY, pRdetZ, nDetXMap, nDetYMap);
  181. // Pixel start index and increment
  182. int detIstart = 0;
  183. int detIinc = 1;
  184. // Mapped detector length
  185. float deltaDetmY = pDetmY[detIstart + detIinc] - pDetmY[detIstart];
  186. // For each slice
  187. for (int z = 0; z < nSlices; z++) {
  188. // Tmp Z coords value for Y direction
  189. float* const pObjZt = (float*)mxMalloc(nPixYMap * sizeof(float));
  190. // Tmp Pixel X mapped coords
  191. float* const pPixmXt = (float*)mxMalloc(nPixYMap * nPixXMap * sizeof(float));
  192. // Get specif Z coord value for each slice
  193. for (int k = 0; k < nPixYMap; k++) pObjZt[k] = pObjZ[z];
  194. // Map slice onto XY plane
  195. mapp2xy(pPixmXt, pPixmY, tubeX, rtubeY, rtubeZ, pObjX, pObjY, pObjZt, nPixXMap, nPixYMap);
  196. // Flip X(Img coord is reverse to Global)
  197. for (int x = 0, x_inv = nPixXMap - 1; x < nPixXMap; x++, x_inv--)
  198. pPixmX[x] = pPixmXt[x_inv*nPixYMap];
  199. // Free temp variables
  200. mxFree(pObjZt);
  201. mxFree(pPixmXt);
  202. // Pixel start index and increment
  203. int pixIstart = 0;
  204. int pixIinc = 1;
  205. // Mapped pixel length
  206. float deltaPixmX = pPixmX[pixIstart + pixIinc] - pPixmX[pixIstart];
  207. float deltaPixmY = pPixmY[pixIstart + pixIinc] - pPixmY[pixIstart];
  208. // Start pixel and detector indices
  209. int detIndY = detIstart;
  210. int pixIndY = pixIstart;
  211. // Case 1
  212. // Find first detector overlap maped with pixel maped on Y
  213. if (pDetmY[detIndY] - pPixmY[pixIstart] < -deltaDetmY)
  214. while (pDetmY[detIndY] - pPixmY[pixIstart] < -deltaDetmY)
  215. detIndY = detIndY + detIinc;
  216. else
  217. // Case 2
  218. // Find first pixel overlap maped with detector maped on Y
  219. if (pDetmY[detIstart] - pPixmY[pixIndY] > deltaPixmY)
  220. while (pDetmY[detIstart] - pPixmY[pixIndY] > deltaPixmY)
  221. pixIndY = pixIndY + pixIinc;
  222. float moving_left_boundaryY = NULL;
  223. // Get the left coordinate of the first overlap on Y axis
  224. if (pDetmY[detIndY] < pPixmY[pixIndY])
  225. moving_left_boundaryY = pPixmY[pixIndY];
  226. else
  227. moving_left_boundaryY = pDetmY[detIndY];
  228. // Allocate memory for specif row of X map detector coords
  229. float* const pDetmXrow = (float*)mxMalloc(nDetXMap * sizeof(float));
  230. float overLapY = NULL;
  231. // Loop over Y intersections
  232. while ((detIndY < nDetY) && (pixIndY < nPixY)) {
  233. float alpha = (float)atan((pDetmY[detIndY] + (deltaDetmY / 2) - rtubeY) / rtubeZ);
  234. // Case A, when you jump to the next detector boundarie but stay
  235. // in the same pixel
  236. if (pDetmY[detIndY + 1] <= pPixmY[pixIndY + 1])
  237. overLapY = (pDetmY[detIndY + 1] - moving_left_boundaryY) / deltaDetmY; // Normalized overlap Calculation
  238. else
  239. // Case B, when you jump to the next pixel boundarie but stay
  240. // in the same detector
  241. overLapY = (pPixmY[pixIndY + 1] - moving_left_boundaryY) / deltaDetmY; // Normalized overlap Calculation
  242. // ***** X overlap *****
  243. int detIndX = detIstart;
  244. int pixIndX = pixIstart;
  245. // Get row / coll of X flipped, which correspond to that Y overlap det
  246. for (int x = 0, x_inv = nDetXMap - 1; x < nDetXMap; x++, x_inv--)
  247. pDetmXrow[x] = pDetmX[(x_inv*nDetYMap) + detIndY];
  248. // Mapped detecor length on X
  249. float deltaDetmX = pDetmXrow[detIstart + detIinc] - pDetmXrow[detIstart];
  250. // Case 1
  251. // Find first detector overlap maped with pixel maped on X
  252. if (pDetmXrow[detIndX] - pPixmX[pixIstart] < -deltaDetmX)
  253. while (pDetmXrow[detIndX] - pPixmX[pixIstart] < -deltaDetmX)
  254. detIndX = detIndX + detIinc;
  255. else
  256. // Case 2
  257. // Find first pixel overlap maped with detector maped on X
  258. if (pDetmXrow[detIstart] - pPixmX[pixIndX] > deltaPixmX)
  259. while (pDetmXrow[detIstart] - pPixmX[pixIndY] > deltaPixmX)
  260. pixIndX = pixIndX + pixIinc;
  261. float moving_left_boundaryX = NULL;
  262. // Get the left coordinate of the first overlap on X axis
  263. if (pDetmXrow[detIndX] < pPixmX[pixIndX])
  264. moving_left_boundaryX = pPixmX[pixIndX];
  265. else
  266. moving_left_boundaryX = pDetmXrow[detIndX];
  267. // Loop over X intersections
  268. while ((detIndX < nDetX) && (pixIndX < nPixX)) {
  269. float gamma = (float)atan((pDetmXrow[detIndX] + (deltaDetmX / 2) - tubeX) / rtubeZ);
  270. // Case A, when you jump to the next detector boundarie but stay
  271. // in the same pixel
  272. if (pDetmXrow[detIndX + 1] <= pPixmX[pixIndX + 1]) {
  273. float overLapX = (pDetmXrow[detIndX + 1] - moving_left_boundaryX) / deltaDetmX; // Normalized overlap Calculation
  274. pVolumet[(z*nPixX*nPixY) + (pixIndX*nPixY) + pixIndY] += overLapX * overLapY * pProjf[(p*nDetX*nDetY) + (detIndX *nDetY) + detIndY] * dz / ((float)cos(alpha)*(float)cos(gamma));
  275. detIndX = detIndX + detIinc;
  276. moving_left_boundaryX = pDetmXrow[detIndX];
  277. }
  278. else {
  279. // Case B, when you jump to the next pixel boundarie but stay
  280. // in the same detector
  281. float overLapX = (pPixmX[pixIndX + 1] - moving_left_boundaryX) / deltaDetmX; // Normalized overlap Calculation
  282. pVolumet[(z*nPixX*nPixY) + (pixIndX*nPixY) + pixIndY] += overLapX * overLapY * pProjf[(p*nDetX*nDetY) + (detIndX *nDetY) + detIndY] * dz / ((float)cos(alpha)*(float)cos(gamma));
  283. pixIndX = pixIndX + pixIinc;
  284. moving_left_boundaryX = pPixmX[pixIndX];
  285. }
  286. }
  287. // ***** Back to Y overlap *****
  288. // Case A, when you jump to the next detector boundarie but stay
  289. // in the same pixel
  290. if (pDetmY[detIndY + 1] <= pPixmY[pixIndY + 1]) {
  291. detIndY = detIndY + detIinc;
  292. moving_left_boundaryY = pDetmY[detIndY];
  293. }
  294. else {
  295. // Case B, when you jump to the next pixel boundarie but stay
  296. // in the same detector
  297. pixIndY = pixIndY + pixIinc;
  298. moving_left_boundaryY = pPixmY[pixIndY];
  299. }
  300. } // Y Overlap loop
  301. // Free memory
  302. mxFree(pDetmXrow);
  303. } // Loop end slices
  304. } // Loop end Projections
  305. // Free memory
  306. mxFree(pProjf);
  307. mxFree(pDetX);
  308. mxFree(pDetY);
  309. mxFree(pDetZ);
  310. mxFree(pObjX);
  311. mxFree(pObjY);
  312. mxFree(pObjZ);
  313. mxFree(pDetmY);
  314. mxFree(pDetmX);
  315. mxFree(pPixmX);
  316. mxFree(pPixmY);
  317. mxFree(pRdetY);
  318. mxFree(pRdetZ);
  319. // Output memory allocation
  320. mwSize dims[3] = { nPixY,nPixX,nSlices };
  321. plhs[0] = mxCreateNumericArray(3, dims, mxSINGLE_CLASS, mxREAL);
  322. float * pVolume = (float*)mxGetData(plhs[0]);
  323. // Flip volume back X (Img coord is reverse to Global)
  324. for (int z = 0; z < nSlices; z++)
  325. for (int x = 0, x_inv = nPixX - 1; x < nPixX; x++, x_inv--)
  326. for (int y = 0; y < nPixY; y++)
  327. pVolume[(z*nPixX*nPixY) + (x*nPixY) + y] = pVolumet[(z*nPixX*nPixY) + (x_inv*nPixY) + y] / (float) nProj;
  328. mxFree(pVolumet);
  329. return;
  330. }
  331. // Map on XY plane
  332. void mapp2xy(float* const pXmapp,
  333. float* const pYmapp,
  334. float tubeX,
  335. float tubeY,
  336. float tubeZ,
  337. float * const pXcoord,
  338. float * const pYcoord,
  339. float * const pZcoord,
  340. const int nXelem,
  341. const int nYelem) {
  342. for (int x = 0; x < nXelem; x++)
  343. for (int y = 0; y < nYelem; y++) {
  344. int ind = (x*nYelem) + y;
  345. pXmapp[ind] = pXcoord[x] + pZcoord[y] * (pXcoord[x] - tubeX) / (tubeZ - pZcoord[y]);
  346. if (x == 0)
  347. pYmapp[y] = pYcoord[y] + pZcoord[y] * (pYcoord[y] - tubeY) / (tubeZ - pZcoord[y]);
  348. }
  349. return;
  350. }