backprojectionDD.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. %% Author: Rodrigo de Barros Vimieiro
  2. % Date: July, 2018
  3. % rodrigo.vimieiro@gmail.com
  4. % =========================================================================
  5. %{
  6. % -------------------------------------------------------------------------
  7. % backprojectionDD(proj,param)
  8. % -------------------------------------------------------------------------
  9. % DESCRIPTION:
  10. % This function reconstruct the 3D volume from projections, based on
  11. % the Distance-Driven principle. It works by calculating the overlap
  12. % in X and Y axis of the volume and the detector boundaries.
  13. % The geometry is for DBT with half cone-beam. All parameters are set
  14. % in "ParameterSettings" code.
  15. %
  16. % INPUT:
  17. %
  18. % - proj = 2D projections for each angle
  19. % - param = Parameter of all geometry
  20. % - projNumber = Vector with projections numbers to be processed
  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. function data3d = backprojectionDD(proj,param,projNumber)
  52. % Stack of reconstructed slices
  53. data3d = zeros(param.ny, param.nx, param.nz,'single');
  54. % Map detector and object boudaries
  55. param.us = (param.nu:-1:0)*param.du;
  56. param.vs = (-(param.nv)/2:1:(param.nv)/2)*param.dv;
  57. param.xs = (param.nx:-1:0)*param.dx;
  58. param.ys = (-(param.ny)/2:1:(param.ny)/2)*param.dy;
  59. param.zs = (0:1:param.nz-1)*param.dz + param.DAG + (param.dz/2);
  60. % Detector boudaries coordinate sytem in (mm)
  61. [detX,detY] = meshgrid(param.us,param.vs);
  62. detZ = zeros(size(detX));
  63. % Object boudaries coordinate sytem in (mm)
  64. [objX,objY] = meshgrid(param.xs,param.ys);
  65. objZ = param.zs; % Z object coordinates
  66. % X-ray tube initial position
  67. tubeX = 0;
  68. tubeY = 0;
  69. tubeZ = param.DSD;
  70. % Iso-center position
  71. isoY = 0;
  72. isoZ = param.DDR;
  73. % Projection angles
  74. tubeAngle = deg2rad(param.tubeDeg);
  75. detAngle = deg2rad(param.detectorDeg);
  76. % Number of detector and voxels for each direction
  77. nDetX = param.nu;
  78. nDetY = param.nv;
  79. nPixX = param.nx;
  80. nPixY = param.ny;
  81. nSlices = param.nz;
  82. nProjs = param.nProj;
  83. % Voxel size on Z
  84. pixsZ = param.dz;
  85. % Test if there's specific angles
  86. if(isempty(projNumber))
  87. projNumber = 1:nProjs;
  88. else
  89. if(max(projNumber(:)) <= nProjs)
  90. nProjs = size(projNumber,2);
  91. else
  92. error('Projection number exceeds the maximum for the equipment.')
  93. end
  94. end
  95. proj = fliplr(proj);
  96. % For each projection
  97. for p=1:nProjs
  98. % Get specific projection number
  99. projN = projNumber(p);
  100. % Get specifc projection for specif angle
  101. projAngle = proj(:,:,p);
  102. % Get specif tube angle for the projection
  103. theta = tubeAngle(projN);
  104. % Get specif detector angle for the projection
  105. phi = detAngle(projN);
  106. % Tubre rotation
  107. rtubeY = ((tubeY - isoY)*cos(theta)-(tubeZ - isoZ)*sin(theta) )+isoY;
  108. rtubeZ = ((tubeY - isoY)*sin(theta)+(tubeZ - isoZ)*cos(theta) )+isoZ;
  109. % Detector rotation
  110. rdetY = ((detY - isoY).*cos(phi)-(detZ - isoZ).*sin(phi) )+isoY;
  111. rdetZ = ((detY - isoY).*sin(phi)+(detZ - isoZ).*cos(phi) )+isoZ;
  112. % Map detector onto XY plane(Inside proj loop in case detector rotates)
  113. [detmX,detmY] = mapp2xy(tubeX,rtubeY,rtubeZ,detX,rdetY,rdetZ);
  114. % Z coord does not change in X direction, so we can take only one
  115. % collumn of Y mapped detecotr boundaries
  116. detmY = detmY(:,1);
  117. % Detector start index and increment
  118. detIstart = 1;
  119. detIinc = 1;
  120. % Mapped detector length
  121. deltaDetmY = detmY(detIstart+detIinc)-detmY(detIstart);
  122. % For each slice
  123. for nz=1:nSlices
  124. % Temporary variable for each slice
  125. slice = zeros(nPixY,nPixX);
  126. % Map slice onto XY plane
  127. [pixmX,pixmY] = mapp2xy(tubeX,rtubeY,rtubeZ,objX,objY,objZ(nz));
  128. % - Z coord does not change in one slice, so we can take just one
  129. % line of mapped coordinates per slice
  130. % - Flip X (Img coord is reverse to Global)
  131. pixmX = pixmX(1,end:-1:1);
  132. pixmY = pixmY(:,1);
  133. % Pixel start index and increment
  134. pixIstart = 1;
  135. pixIinc = 1;
  136. % Mapped pixel length
  137. deltaPixmX = pixmX(pixIstart+pixIinc)-pixmX(pixIstart);
  138. deltaPixmY = pixmY(pixIstart+pixIinc)-pixmY(pixIstart);
  139. % Start pixel and detector indices
  140. detIndY = detIstart;
  141. pixIndY = pixIstart;
  142. % Case 1
  143. % Find first detector overlap maped with pixel maped on Y
  144. if(detmY(detIndY)-pixmY(pixIstart)<-deltaDetmY)
  145. while(detmY(detIndY)-pixmY(pixIstart)<-deltaDetmY)
  146. detIndY = detIndY + detIinc;
  147. end
  148. else
  149. % Case 2
  150. % Find first pixel overlap maped with detector maped on Y
  151. if(detmY(detIstart)-pixmY(pixIndY)>deltaPixmY)
  152. while(detmY(detIstart)-pixmY(pixIndY)>deltaPixmY)
  153. pixIndY = pixIndY + pixIinc;
  154. end
  155. end
  156. end
  157. % Get the left coordinate of the first overlap on Y axis
  158. % Try the following lines for a better understanding
  159. % % ---------------------------------------------------------------
  160. % % figure
  161. % % plot(detmY(:),zeros(1,size(detmY,1)),'r.','MarkerSize',6)
  162. % % hold on
  163. % % plot(pixmY(:),zeros(1,size(pixmY,1)),'b.','MarkerSize',6)
  164. % % hold off
  165. % % legend('Detector Boundaries','Pixel Boundaries')
  166. % % title('Overlap on Y axis')
  167. % % ---------------------------------------------------------------
  168. if( detmY(detIndY) < pixmY(pixIndY) )
  169. moving_left_boundaryY = pixmY(pixIndY);
  170. else
  171. moving_left_boundaryY = detmY(detIndY);
  172. end
  173. % Loop over Y intersections
  174. while((detIndY<=nDetY)&&(pixIndY<=nPixY))
  175. alpha = atan((detmY(detIndY)+(deltaDetmY/2)-rtubeY)/rtubeZ);
  176. % Case A, when you jump to the next detector boundarie but stay
  177. % in the same pixel
  178. if(detmY(detIndY+1)<=pixmY(pixIndY+1))
  179. overLapY = (detmY(detIndY+1)- moving_left_boundaryY)...
  180. /deltaDetmY; % Normalized overlap Calculation
  181. else
  182. % Case B, when you jump to the next pixel boundarie but stay
  183. % in the same detector
  184. overLapY = (pixmY(pixIndY+1)- moving_left_boundaryY)...
  185. /deltaDetmY; % Normalized overlap Calculation
  186. end
  187. %% X overlap
  188. detIndX = detIstart;
  189. pixIndX = pixIstart;
  190. % Get row/coll of X, which correspond to that Y overlap det
  191. detmXrow = detmX(detIndY,end:-1:1);
  192. % Mapped detecor length on X
  193. deltaDetmX = detmXrow(detIstart+detIinc)-detmXrow(detIstart);
  194. % Case 1
  195. % Find first detector overlap maped with pixel maped on X
  196. if(detmXrow(detIndX)-pixmX(pixIstart)<-deltaDetmX)
  197. while(detmXrow(detIndX)-pixmX(pixIstart)<-deltaDetmX)
  198. detIndX = detIndX + detIinc;
  199. end
  200. else
  201. % Case 2
  202. % Find first pixel overlap maped with detector maped on X
  203. if(detmXrow(detIstart)-pixmX(pixIndX)>deltaPixmX)
  204. while(detmXrow(detIstart)-pixmX(pixIndY)>deltaPixmX)
  205. pixIndX = pixIndX + pixIinc;
  206. end
  207. end
  208. end
  209. % Get the left coordinate of the first overlap on X axis
  210. % Try the following lines for a better understanding
  211. % % ---------------------------------------------------------------
  212. % % figure
  213. % % plot(detmXrow(:),zeros(1,size(detmXrow,2)),'r.','MarkerSize',6)
  214. % % hold on
  215. % % plot(pixmX(:),zeros(1,size(pixmX,1)),'b.','MarkerSize',6)
  216. % % hold off
  217. % % legend('Detector Boundaries','Pixel Boundaries')
  218. % % title('Overlap on X axis')
  219. % % ---------------------------------------------------------------
  220. if( detmXrow(detIndX) < pixmX(pixIndX) )
  221. moving_left_boundaryX = pixmX(pixIndX);
  222. else
  223. moving_left_boundaryX = detmXrow(detIndX);
  224. end
  225. % Loop over X intersections
  226. while((detIndX<=nDetX)&&(pixIndX<=nPixX))
  227. gamma = atan((detmXrow(detIndX)+(deltaDetmX/2)-tubeX)/rtubeZ);
  228. % Case A, when you jump to the next detector boundarie but stay
  229. % in the same pixel
  230. if(detmXrow(detIndX+1)<=pixmX(pixIndX+1))
  231. overLapX = (detmXrow(detIndX+1)- moving_left_boundaryX)...
  232. /deltaDetmX; % Normalized overlap Calculation
  233. slice((pixIndX-1)*nPixY+pixIndY) = ...
  234. slice((pixIndX-1)*nPixY+pixIndY) ...
  235. + overLapX * overLapY * projAngle((detIndX-1)*nDetY+detIndY)...
  236. * pixsZ/(cos(alpha)*cos(gamma));
  237. detIndX = detIndX + detIinc;
  238. moving_left_boundaryX = detmXrow(detIndX);
  239. else
  240. % Case B, when you jump to the next pixel boundarie but stay
  241. % in the same detector
  242. overLapX = (pixmX(pixIndX+1)- moving_left_boundaryX)...
  243. /deltaDetmX; % Normalized overlap Calculation
  244. slice((pixIndX-1)*nPixY+pixIndY) = ...
  245. slice((pixIndX-1)*nPixY+pixIndY)...
  246. + overLapX * overLapY * projAngle((detIndX-1)*nDetY+detIndY)...
  247. * pixsZ/(cos(alpha)*cos(gamma));
  248. pixIndX = pixIndX + pixIinc;
  249. moving_left_boundaryX = pixmX(pixIndX);
  250. end
  251. end
  252. %% Back to Y overlap
  253. % Case A, when you jump to the next detector boundarie but stay
  254. % in the same pixel
  255. if(detmY(detIndY+1)<=pixmY(pixIndY+1))
  256. detIndY = detIndY + detIinc;
  257. moving_left_boundaryY = detmY(detIndY);
  258. else
  259. % Case B, when you jump to the next pixel boundarie but stay
  260. % in the same detector
  261. pixIndY = pixIndY + pixIinc;
  262. moving_left_boundaryY = pixmY(pixIndY);
  263. end
  264. end % Y Overlap loop
  265. % Accumulate for each proj angle
  266. data3d(:,:,nz) = data3d(:,:,nz) + fliplr(slice);
  267. end % Loop end slices
  268. end % Loop end Projections
  269. data3d = data3d ./ nProjs;
  270. end
  271. %% Map on XY plane
  272. function [x,y] = mapp2xy(x1,y1,z1,x2,y2,z2)
  273. x = x2 + z2 .* (x2-x1)./(z1-z2);
  274. y = y2 + z2 .* (y2-y1)./(z1-z2);
  275. end