vol_prep.m 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. function vol_prep
  2. % this function calls three other functions:
  3. % - vol_prep_bin
  4. % - vol_prep_fuzzy
  5. % - vol_prep_DPmap
  6. % that are needed for expanding the initial segmentations done in the
  7. % segmentation GUI
  8. fprintf('Starting binary volume expansion...')
  9. vol_prep_bin
  10. fprintf(' done!\n')
  11. fprintf('Starting fuzzy volume expansion...')
  12. vol_prep_fuzzy
  13. fprintf(' done!\n')
  14. fprintf('Creating dose plans ...')
  15. vol_prep_DPmap
  16. fprintf(' done!\n')
  17. end
  18. function vol_prep_bin
  19. % this function takes in the segmentations and creates PTV/GTV/CTV binary volumes,
  20. % as well as appropriately cropped healthy tissue (body) segmentation.
  21. CTV_margin = 0.5; % GTV->CTV margin, in cm
  22. PTV_margin = 0.5; % GTV->CTV margin, in cm
  23. body_margin = 7.0; % how far from tumor do we still include body, in cm
  24. pathIn = '\\Mpufs5\data_wnx1\_Data\Glioma_aus\FET_FGL005\B1\Processed\';
  25. [CT_in, CT_meta] = nrrdread([pathIn 'FET_FGL005_B1_CT2FET.nrrd']);
  26. [GTV_in, GTV_meta] = nrrdread([pathIn 'B1_seg\FET_FGL005_B1_seg_thr2.0.nrrd']);
  27. [body_in, body_meta] = nrrdread([pathIn 'B1_seg\FET_FGL005_B1_head.nrrd']);
  28. %% identify target voxels - these are used when identifiying valid beamlets
  29. % voxels that need to be considered (above the threshold)
  30. p_threshold = 0;
  31. GTV_binary = zeros(size(GTV_in));
  32. GTV_binary(GTV_in>p_threshold) = 1;
  33. % Account for infiltration (GTV -> CTV)
  34. CTV = zeros(size(GTV_binary));
  35. bwD = bwdistsc(GTV_binary, GTV_meta.spacedirections);
  36. CTV(bwD<CTV_margin) = 1;
  37. % expand to PTV (CTV -> PTV)
  38. PTV = zeros(size(GTV_binary));
  39. bwD = bwdistsc(GTV_binary, GTV_meta.spacedirections);
  40. PTV(bwD<PTV_margin) = 1;
  41. %% identify body volume of interest
  42. body = body_in;
  43. bwD = bwdistsc(GTV_binary, GTV_meta.spacedirections);
  44. body(PTV>0) = 0;
  45. body(bwD>body_margin) = 0; % how far from the tumor are we still interested in the body
  46. %% save outputs
  47. filename = [pathIn 'B1_seg\FET_FGL005_B1_seg_thr2.0_binPTV.nrrd'];
  48. matrix = single(PTV);
  49. pixelspacing = GTV_meta.spacedirections;
  50. origin = GTV_meta.spaceorigin;
  51. nrrdWriter(filename, matrix, pixelspacing, origin, 'raw');
  52. filename = [pathIn 'B1_seg\FET_FGL005_B1_head_crop_bin.nrrd'];
  53. matrix = single(body);
  54. pixelspacing = body_meta.spacedirections;
  55. origin = body_meta.spaceorigin;
  56. nrrdWriter(filename, matrix, pixelspacing, origin, 'raw');
  57. colorwash(CT_in, PTV, [-500, 500], [0, 1.2]);
  58. colorwash(CT_in, body, [-500, 500], [0, 1.2]);
  59. end
  60. function vol_prep_fuzzy
  61. % this function takes in the segmentations and creates fuzzy (probabilistic)
  62. % PTV/GTV/CTV volumes
  63. infilt_range = 1.2; % range of infiltration, in cm. Should be value of sigma (or 1/3 decay range). Assuming normal infil.
  64. pathIn = '\\Mpufs5\data_wnx1\_Data\Glioma_aus\FET_FGL005\B1\Processed\';
  65. [CT_in, CT_meta] = nrrdread([pathIn 'FET_FGL005_B1_CT2FET.nrrd']);
  66. [GTV_in, GTV_meta] = nrrdread([pathIn 'B1_seg\FET_FGL005_B1_seg_combined.nrrd']);
  67. [body_in, body_meta] = nrrdread([pathIn 'B1_seg\FET_FGL005_B1_head.nrrd']);
  68. % colorwash(CT_in, GTV_in, [-500, 500], [0, 1.2]);
  69. %% Account for infiltration (GTV -> CTV)
  70. % -- get distance from central dot --
  71. ir_a = 1.5; % infiltration range factor
  72. CTV = zeros(size(GTV_in));
  73. bwD = bwdistsc(GTV_in>0.05, GTV_meta.spacedirections);
  74. CTV(bwD< (ir_a * infilt_range) ) = 1;
  75. % for each voxel in the area of interest, figure out the highest
  76. % probability based on range from tumor * p that tumor is there
  77. GTV_idx_lst = find(GTV_in >0.05);
  78. CTV_idx_lst = find(CTV >0.0);
  79. prob_CTV = zeros(size(GTV_in));
  80. f = waitbar(0,'calculating expansion');
  81. % define area of interest
  82. Ksize = 1+2*ceil((ir_a * infilt_range)/min(GTV_meta.spacedirections)); % get kernel for convolution.
  83. kernel_base = zeros(Ksize,Ksize,Ksize);
  84. kernel_base(ceil(Ksize/2),ceil(Ksize/2),ceil(Ksize/2))=1;
  85. bwD = bwdistsc(kernel_base, GTV_meta.spacedirections);
  86. kernel = zeros(size(kernel_base));
  87. kernel(bwD< (ir_a * infilt_range)) = 1;
  88. CTV_idx_lst_num = numel(CTV_idx_lst);
  89. prob_CTV_solutions = zeros(1,CTV_idx_lst_num);
  90. tic
  91. for i = 1:CTV_idx_lst_num
  92. i_idx = CTV_idx_lst(i);
  93. [y1, x1, z1] = ind2sub(size(GTV_in), i_idx);
  94. waitbar(i/numel(CTV_idx_lst),f);
  95. % get the crop of nearby voxels in tumor
  96. % tabula = zeros(size(GTV_in));
  97. % tabula(i_idx) = 1;
  98. % tabula2 = convn(tabula, kernel, 'same');
  99. % idx_nearby = find(tabula2>0);
  100. idx_nearby = f_neighbors(i_idx, size(GTV_in), ir_a, infilt_range, GTV_meta.spacedirections);
  101. % overlap
  102. idx_GTV_nearby = intersect (idx_nearby, GTV_idx_lst);
  103. if numel(idx_GTV_nearby) <1
  104. prob_CTV_solutions(i) = 0;
  105. error('this is fucky. no voxels within range')
  106. end
  107. p_list=zeros(1, numel(idx_GTV_nearby));
  108. p_list_max = 0;
  109. for j = 1:numel(idx_GTV_nearby)
  110. j_idx = idx_GTV_nearby(j);
  111. if GTV_in(j_idx) < p_list_max
  112. continue
  113. end
  114. [y2, x2, z2] = ind2sub(size(GTV_in), j_idx);
  115. d = sqrt((((y2-y1)*GTV_meta.spacedirections(1))^2 + ...
  116. ((x2-x1)*GTV_meta.spacedirections(2))^2 + ((z2-z1)*GTV_meta.spacedirections(3))^2));
  117. % get probability of invisible infiltration at given voxel, based
  118. % on single voxel from tumor
  119. p_list(j) = GTV_in(j_idx) * exp(-(d*d)/(infilt_range*infilt_range));
  120. p_list_max = max(p_list_max, p_list(j));
  121. if p_list(j) == 1
  122. prob_CTV_solutions(i) = 1;
  123. break
  124. end
  125. end
  126. prob_CTV_solutions(i) = max(p_list);
  127. end
  128. toc
  129. prob_CTV(CTV_idx_lst) = prob_CTV_solutions;
  130. close(f)
  131. colorwash(CT_in, GTV_in, [-500, 500], [0, 1.2]);
  132. colorwash(CT_in, prob_CTV, [-500, 500], [0, 1.2]);
  133. %% save outputs
  134. filename = [pathIn 'RODP_files\FET_FGL005_B1_seg_fuzzyCTV.nrrd'];
  135. matrix = single(prob_CTV);
  136. pixelspacing = GTV_meta.spacedirections;
  137. origin = GTV_meta.spaceorigin;
  138. nrrdWriter(filename, matrix, pixelspacing, origin, 'raw');
  139. end
  140. function neigh_mat_idx = f_neighbors(idx, mat_size, ir_a, infilt_range, voxsize);
  141. % this function returns indeces of the box nearby around
  142. [y, x, z] = ind2sub(mat_size, idx);
  143. y1 = max(1, floor(y - ir_a*infilt_range/(voxsize(1))));
  144. y2 = min(mat_size(1), ceil(y + ir_a*infilt_range/(voxsize(1))));
  145. x1 = max(1, floor(x - ir_a*infilt_range/(voxsize(2))));
  146. x2 = min(mat_size(1), ceil(x + ir_a*infilt_range/(voxsize(2))));
  147. z1 = max(1, floor(z - ir_a*infilt_range/(voxsize(3))));
  148. z2 = min(mat_size(1), ceil(z + ir_a*infilt_range/(voxsize(3))));
  149. tabula = zeros(mat_size);
  150. tabula(y1:y2, x1:x2, z1:z2)=1;
  151. neigh_mat_idx = find( tabula>0);
  152. end
  153. function vol_prep_DPmap
  154. % this function takes in the segmentations and creates fuzzy (probabilistic)
  155. % PTV/GTV/CTV volumes
  156. D_min = 80;
  157. D_max = 83;
  158. pathIn = '\\Mpufs5\data_wnx1\_Data\Glioma_aus\FET_FGL005\B1\Processed\';
  159. [CT_in, CT_meta] = nrrdread([pathIn 'FET_FGL005_B1_CT2FET.nrrd']);
  160. [CTV_fuzzy, CTV_meta] = nrrdread([pathIn 'RODP_files\FET_FGL005_B1_seg_fuzzyCTV.nrrd']);
  161. dose_min = f_dose_min(CTV_fuzzy, D_min);
  162. dose_max = f_dose_max(CTV_fuzzy, D_max);
  163. orthoslice(dose_min, [0, D_max+10]) % plot min dose boundary
  164. orthoslice(dose_max, [0, D_max+10]) % plot max dose boundary
  165. %% save outputs
  166. filename = [pathIn 'RODP_files\FET_FGL005_B1_DP_minDose.nrrd'];
  167. matrix = single(dose_min);
  168. pixelspacing = CTV_meta.spacedirections;
  169. origin = CTV_meta.spaceorigin;
  170. nrrdWriter(filename, matrix, pixelspacing, origin, 'raw');
  171. filename = [pathIn 'RODP_files\FET_FGL005_B1_DP_maxDose.nrrd'];
  172. matrix = single(dose_max);
  173. pixelspacing = CTV_meta.spacedirections;
  174. origin = CTV_meta.spaceorigin;
  175. nrrdWriter(filename, matrix, pixelspacing, origin, 'raw');
  176. end
  177. function dose_min = f_dose_min(CTV_fuzzy, D_min)
  178. % get low boundary for ideal dose plan
  179. dose_min = D_min* min(1, 2* max(0, (CTV_fuzzy - 0.25)));
  180. % orthoslice(dose_min)
  181. end
  182. function dose_max = f_dose_max(CTV_fuzzy, D_max)
  183. % get hihg boundary for ideal dose plan
  184. dose_max = D_max* min(1, 2* max(0, (CTV_fuzzy - 0.0)));
  185. end