projectionDD.m 12 KB

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