SliceViewerPanel.m 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. classdef SliceViewerPanel < handle
  2. %SliceViewerPanel uipanel viewer to SliceViewerPanelSource
  3. %
  4. % SliceViewerPanel plots the content from SliceViewerPanelSource (SVPS).
  5. %
  6. % Coordinate/Orientation in detail:
  7. % zOrient = 1: Coronal, abscissa = dim3 = zmesh, ordinate = dim2 = ymesh
  8. % zOrient = 2: Sagittal, abscissa = dim3 = zmesh, ordinate = dim1 = xmesh
  9. % zOrient = 3: Transverse, abscissa = dim1 = xmesh, ordinate = dim2 = ymesh
  10. % or think as this:
  11. % zOrient = 1: image(zmesh, ymesh, Geometry.rhomw(slice,:,:))
  12. % zOrient = 2: image(zmesh, xmesh, Geometry.rhomw(:,slice,:))
  13. % zOrient = 3: image(xmesh, ymesh, Geometry.rhomw(:,:,slice))
  14. %
  15. % See also XTPS
  16. %
  17. % Author: Xiaohu Mo
  18. %===============================================================================
  19. properties
  20. hSVPS % handle to SliceViewerPanelSource
  21. zOrien % scroll along which dimension: 1=S; 2=C; 3=T
  22. % TODO should be private
  23. % handles to gui objects
  24. hPanel
  25. hAxis
  26. hSlider
  27. hEdit
  28. hLineH % horizontal TCS line
  29. hLineV % vertical TCS line
  30. hImage % image has CT and dose overlayed manually
  31. hCbar % colorbar handle
  32. hROIs % handle vector of overlay ROIs
  33. hDoseContours % handle vector of overlay isodose lines
  34. hDoseLegend % handle of legend of isodose lines
  35. end
  36. %===============================================================================
  37. %===============================================================================
  38. methods
  39. %-------------------------------------------------------------------------------
  40. function obj = SliceViewerPanel(hSVPS, zOrien, hPanelParent) % Constructor returns object
  41. obj.hSVPS = hSVPS;
  42. obj.zOrien = zOrien;
  43. obj.hPanel = uipanel('Parent', hPanelParent);
  44. obj.hAxis = axes('Parent', obj.hPanel);
  45. obj.hSlider = uicontrol(obj.hPanel, 'Style','slider');
  46. obj.hEdit = uicontrol(obj.hPanel, 'Style','edit');
  47. obj.hImage = image('Parent', obj.hAxis);
  48. obj.hLineH = line('Parent', obj.hAxis, 'XData', [], 'YData', []);
  49. obj.hLineV = line('Parent', obj.hAxis, 'XData', [], 'YData', []);
  50. obj.hCbar = [];
  51. obj.hDoseContours = [];
  52. obj.hDoseLegend = [];
  53. % Callback passes 3 args: (obj, src, evt)
  54. set(obj.hSlider, 'Callback', @obj.Slider_Callback);
  55. set(obj.hEdit, 'Callback', @obj.Edit_Callback);
  56. % Properties
  57. axis(obj.hAxis, 'off');
  58. set(obj.hImage, 'CData', [], 'CDataMapping', 'scaled');
  59. set(obj.hLineH, 'HitTest', 'off', 'Color', 'red');
  60. set(obj.hLineV, 'HitTest', 'off', 'Color', 'red');
  61. set([obj.hPanel obj.hSlider obj.hEdit], ...
  62. 'Background', get(get(obj.hPanel, 'Parent'), 'Color'));
  63. % Add Listener
  64. addlistener(obj.hSVPS, 'POI', 'PostSet', @obj.handleEvent);
  65. addlistener(obj.hSVPS, 'Geometry', 'PostSet', @obj.handleEvent);
  66. addlistener(obj.hSVPS, 'dosedisp', 'PostSet', @obj.handleEvent);
  67. addlistener(obj.hSVPS, 'ctWindow', 'PostSet', @obj.handleEvent);
  68. addlistener(obj.hSVPS, 'TCSVisible', 'PostSet', @obj.handleEvent);
  69. end % function SliceViewerPanel
  70. %-------------------------------------------------------------------------------
  71. function set_layout(obj, src, evt)
  72. % Set widget positions and sizes
  73. set([obj.hPanel obj.hAxis obj.hSlider obj.hEdit], 'Unit', 'normalized');
  74. padding = [0.01 0.01];
  75. setPositionXY(obj.hAxis, [0 1], [0.1 1], padding);
  76. setPositionXY(obj.hSlider, [0 0.8], [0 0.1], padding, 1.5, 'characters');
  77. setPositionXY(obj.hEdit, [0.8 1], [0 0.1], padding, 1.5, 'characters');
  78. end
  79. %-------------------------------------------------------------------------------
  80. function handleEvent(obj, src, evt)
  81. switch src.Name
  82. case 'POI'
  83. obj.redraw_lines();
  84. % redraw only changed dimension
  85. if obj.hSVPS.POI(obj.zOrien) ~= obj.hSVPS.prev_POI(obj.zOrien)
  86. obj.redraw_image();
  87. % obj.redraw_contour();
  88. obj.redraw_overlay();
  89. end
  90. case {'Geometry' 'ctWindow' 'ROIdisp' 'dosedisp'}
  91. % redraw all
  92. obj.redraw_image();
  93. % obj.redraw_contour();
  94. obj.redraw_overlay();
  95. case 'TCSVisible'
  96. % redraw line
  97. obj.redraw_lines();
  98. end
  99. end
  100. %-------------------------------------------------------------------------------
  101. function Slider_Callback(obj, src, evt)
  102. % check value, update Edit and Axis
  103. value = round(get(obj.hSlider, 'Value'));
  104. set(obj.hSlider, 'Value', value);
  105. set(obj.hEdit, 'String', num2str(value));
  106. obj.hSVPS.POI(obj.zOrien) = value;
  107. end % function Slider_Callback
  108. %-------------------------------------------------------------------------------
  109. function Edit_Callback(obj, src, evt)
  110. % check value, update Slider and Axis
  111. value = round(str2double(get(obj.hEdit, 'String')));
  112. if value >= get(obj.hSlider, 'Min') && value <= get(obj.hSlider, 'Max')
  113. set(obj.hSlider, 'Value', value);
  114. set(obj.hEdit, 'String', num2str(value));
  115. obj.hSVPS.POI(obj.zOrien) = value;
  116. else % invalid input, set back to slider value
  117. value = get(obj.hSlider, 'Value');
  118. set(obj.hEdit, 'String', num2str(value));
  119. end
  120. end % function Edit_Callback
  121. %-------------------------------------------------------------------------------
  122. function load_data(obj)
  123. set(obj.hSlider, 'Min', 1, 'Max', size(obj.hSVPS.Geometry.rhomw, obj.zOrien), 'Value', 1);
  124. set(obj.hSlider, 'SliderStep', [1 10] / size(obj.hSVPS.Geometry.rhomw, obj.zOrien));
  125. % set image coordinates, axis limits, DataAspectRatio
  126. switch obj.zOrien
  127. case 1
  128. set(obj.hImage, 'XData', obj.hSVPS.Geometry.z, 'YData', obj.hSVPS.Geometry.y);
  129. set(obj.hAxis, 'XLim', [min(obj.hSVPS.Geometry.z) max(obj.hSVPS.Geometry.z)]);
  130. set(obj.hAxis, 'YLim', [min(obj.hSVPS.Geometry.y) max(obj.hSVPS.Geometry.y)]);
  131. zoom(obj.hAxis, 'reset');
  132. daspect(obj.hAxis, [obj.hSVPS.Geometry.voxel_size(3) obj.hSVPS.Geometry.voxel_size(2) 1]);
  133. case 2
  134. set(obj.hImage, 'XData', obj.hSVPS.Geometry.z, 'YData', obj.hSVPS.Geometry.x);
  135. set(obj.hAxis, 'XLim', [min(obj.hSVPS.Geometry.z) max(obj.hSVPS.Geometry.z)]);
  136. set(obj.hAxis, 'YLim', [min(obj.hSVPS.Geometry.x) max(obj.hSVPS.Geometry.x)]);
  137. zoom(obj.hAxis, 'reset');
  138. daspect(obj.hAxis, [obj.hSVPS.Geometry.voxel_size(3) obj.hSVPS.Geometry.voxel_size(1) 1]);
  139. set(obj.hAxis, 'YDir', 'reverse');
  140. case 3
  141. set(obj.hImage, 'XData', obj.hSVPS.Geometry.x, 'YData', obj.hSVPS.Geometry.y);
  142. set(obj.hAxis, 'XLim', [min(obj.hSVPS.Geometry.x) max(obj.hSVPS.Geometry.x)]);
  143. set(obj.hAxis, 'YLim', [min(obj.hSVPS.Geometry.y) max(obj.hSVPS.Geometry.y)]);
  144. zoom(obj.hAxis, 'reset');
  145. daspect(obj.hAxis, [obj.hSVPS.Geometry.voxel_size(1) obj.hSVPS.Geometry.voxel_size(2) 1]);
  146. set(obj.hAxis, 'YDir', 'reverse');
  147. end
  148. % default to volume center
  149. obj.hSVPS.POI = round(size(obj.hSVPS.Geometry.rhomw)/2);
  150. % set slider and edit control when manually set focus point
  151. slice = obj.hSVPS.POI(obj.zOrien);
  152. set(obj.hSlider, 'Value', slice);
  153. set(obj.hEdit, 'String', slice);
  154. end % function load_data
  155. %-------------------------------------------------------------------------------
  156. function updateColorbar(obj)
  157. % only plot color on transvers axis
  158. if obj.zOrien ~= 3; return; end
  159. % delete old one
  160. delete(obj.hCbar); obj.hCbar = [];
  161. switch lower(obj.hSVPS.dosedisp.mode)
  162. case {'isodose' 'isodoseline'}
  163. % get a sorted, valid dose level and colors
  164. valid_indices = find(obj.hSVPS.dosedisp.level > 0);
  165. if isempty(valid_indices); return; end
  166. dose_levels = obj.hSVPS.dosedisp.level(valid_indices);
  167. dose_colors = obj.hSVPS.dosedisp.color(valid_indices,:);
  168. [dose_levels iX] = sort(dose_levels);
  169. dose_labels = arrayfun(@(d)num2str(d), dose_levels, 'UniformOutput', false)';
  170. cmap = dose_colors(iX, :);
  171. colormap(cmap);
  172. set(obj.hAxis, 'CLim', [0 1]);
  173. obj.hCbar = colorbar('Peer', obj.hAxis, 'Location', 'SouthOutside');
  174. set(obj.hCbar, 'TickLength', [0 0], ...
  175. 'XTick', (0.5:numel(dose_levels)-0.5)/numel(dose_levels), ...
  176. 'XTickLabel', dose_labels);
  177. case 'colorwash'
  178. colormap(jet(256));
  179. % set(obj.hAxis, 'CLim', [0 max(obj.hSVPS.optResults.dose{end}(:))]);
  180. % Grozomah
  181. set(obj.hAxis, 'CLim', [0 200]);
  182. obj.hCbar = colorbar('Peer', obj.hAxis, 'Location', 'SouthOutside');
  183. end
  184. end % function updateColorbar
  185. %-------------------------------------------------------------------------------
  186. function updateAxis(obj)
  187. % save axis scaling
  188. % xlim = get(obj.hAxis, 'XLim');
  189. % ylim = get(obj.hAxis, 'YLim');
  190. % % redraw more if slice changed
  191. % if obj.hSVPS.POI(obj.zOrien) ~= obj.hSVPS.prev_POI(obj.zOrien)
  192. % obj.redraw_image();
  193. % obj.redraw_contour();
  194. % end
  195. % obj.redraw_lines();
  196. % restore axis scaling
  197. % set(obj.hAxis, 'XLim', xlim);
  198. % set(obj.hAxis, 'YLim', ylim);
  199. end
  200. %-------------------------------------------------------------------------------
  201. function redraw_lines(obj)
  202. % P1 & P2 are the extent of the grid
  203. P1 = [min(obj.hSVPS.Geometry.y) min(obj.hSVPS.Geometry.x) min(obj.hSVPS.Geometry.z)];
  204. P2 = [max(obj.hSVPS.Geometry.y) max(obj.hSVPS.Geometry.x) max(obj.hSVPS.Geometry.z)];
  205. % F is the coordinate of the intersection of TCS planes
  206. F = [obj.hSVPS.Geometry.y(obj.hSVPS.POI(1)) ...
  207. obj.hSVPS.Geometry.x(obj.hSVPS.POI(2)) ...
  208. obj.hSVPS.Geometry.z(obj.hSVPS.POI(3))];
  209. switch obj.zOrien
  210. case 1
  211. set(obj.hLineH, 'XData', [P1(3) P2(3)], 'YData', [F(2) F(2)]);
  212. set(obj.hLineV, 'XData', [F(3) F(3)], 'YData', [P1(2) P2(2)]);
  213. case 2
  214. set(obj.hLineH, 'XData', [P1(3) P2(3)], 'YData', [F(1) F(1)]);
  215. set(obj.hLineV, 'XData', [F(3) F(3)], 'YData', [P1(1) P2(1)]);
  216. case 3
  217. set(obj.hLineH, 'XData', [P1(2) P2(2)], 'YData', [F(1) F(1)]);
  218. set(obj.hLineV, 'XData', [F(2) F(2)], 'YData', [P1(1) P2(1)]);
  219. end
  220. % Set visibility
  221. if obj.hSVPS.TCSVisible == true
  222. set(obj.hLineH, 'Visible', 'on');
  223. set(obj.hLineV, 'Visible', 'on');
  224. else
  225. set(obj.hLineH, 'Visible', 'off');
  226. set(obj.hLineV, 'Visible', 'off');
  227. end
  228. end
  229. %-------------------------------------------------------------------------------
  230. function redraw_image(obj)
  231. slice = obj.hSVPS.POI(obj.zOrien);
  232. % get CT image
  233. switch obj.zOrien
  234. case 1
  235. ctImage = imagescX((obj.hSVPS.Geometry.data(slice,:,:)), gray(256), obj.hSVPS.ctClim);
  236. case 2
  237. ctImage = imagescX((obj.hSVPS.Geometry.data(:,slice,:)), gray(256), obj.hSVPS.ctClim);
  238. case 3
  239. ctImage = imagescX((obj.hSVPS.Geometry.data(:,:,slice)), gray(256), obj.hSVPS.ctClim);
  240. end
  241. % set image to CT. Will re-set if need to blend with dose later.
  242. set(obj.hImage, 'CData',ctImage);
  243. % redraw dose
  244. delete(obj.hDoseContours);
  245. obj.hDoseContours = [];
  246. if ~isempty(obj.hSVPS.optResults)
  247. switch obj.zOrien
  248. case 1
  249. doseSlice = squeeze(obj.hSVPS.optResults.dose{end}(slice,:,:));
  250. xaxis = obj.hSVPS.Geometry.z;
  251. yaxis = obj.hSVPS.Geometry.y;
  252. case 2
  253. doseSlice = squeeze(obj.hSVPS.optResults.dose{end}(:,slice,:));
  254. xaxis = obj.hSVPS.Geometry.z;
  255. yaxis = obj.hSVPS.Geometry.x;
  256. case 3
  257. doseSlice = squeeze(obj.hSVPS.optResults.dose{end}(:,:,slice));
  258. xaxis = obj.hSVPS.Geometry.x;
  259. yaxis = obj.hSVPS.Geometry.y;
  260. end
  261. switch lower(obj.hSVPS.dosedisp.mode)
  262. case {'isodoseline'}
  263. if ~isempty(nonzeros(obj.hSVPS.dosedisp.level))
  264. hold(obj.hAxis, 'on');
  265. % sort dose levels before contourf
  266. [tilde, sorted_indices]= sort(obj.hSVPS.dosedisp.level);
  267. for idx = sorted_indices(:)'
  268. % scalar of current dose level
  269. dose_level = obj.hSVPS.dosedisp.level(idx);
  270. if dose_level ~= 0
  271. % plot this contour level and append handles to list
  272. % [level level] is used to avoid single level ambiguity of contour
  273. [tilde, obj.hDoseContours(end+1)] = contour(obj.hAxis, ...
  274. xaxis, yaxis, ...
  275. doseSlice, [dose_level dose_level], ...
  276. '-', 'LineWidth', 2, ...
  277. 'LineColor', obj.hSVPS.dosedisp.color(idx, :), ...
  278. 'DisplayName', sprintf('%.1f', dose_level));
  279. end
  280. end
  281. hold(obj.hAxis, 'off');
  282. end
  283. case 'isodose'
  284. % Using transparency with contourf like this fails due to the overlay of patches:
  285. % hPatches = findobj(obj.hDoseContours(end), 'Type', 'patch', '-property', 'FaceAlpha');
  286. % set(hPatches, 'FaceAlpha', obj.hSVPS.dosedisp.alpha(idx));
  287. if ~isempty(nonzeros(obj.hSVPS.dosedisp.level))
  288. doseMap = ones(size(doseSlice));
  289. alphaMap = zeros(size(doseSlice));
  290. % sort dose levels
  291. [tilde, sorted_indices]= sort(obj.hSVPS.dosedisp.level);
  292. for idx = sorted_indices(:)'
  293. % scalar of current dose level
  294. dose_level = obj.hSVPS.dosedisp.level(idx);
  295. % pixels below cmin will have alpha = 0
  296. if dose_level ~= 0
  297. doseMap(doseSlice >= dose_level) = idx;
  298. alphaMap(doseSlice >= dose_level) = obj.hSVPS.dosedisp.alpha(idx);
  299. end
  300. end
  301. % get the color
  302. doseImage = zeros(size(doseSlice,1), size(doseSlice,2), 3);
  303. R = obj.hSVPS.dosedisp.color(:, 1);
  304. G = obj.hSVPS.dosedisp.color(:, 2);
  305. B = obj.hSVPS.dosedisp.color(:, 3);
  306. doseImage(:,:,1) = reshape(R(doseMap), size(doseSlice));
  307. doseImage(:,:,2) = reshape(G(doseMap), size(doseSlice));
  308. doseImage(:,:,3) = reshape(B(doseMap), size(doseSlice));
  309. %
  310. compImage = imblend(doseImage, alphaMap, ctImage, 1);
  311. set(obj.hImage, 'CData', compImage);
  312. end
  313. case 'colorwash'
  314. % Grozomah
  315. % doseImage = imagescX(doseSlice, jet(256), [0 max(obj.hSVPS.optResults.dose{end}(:))]);
  316. doseImage = imagescX(doseSlice, jet(256), [0 max(obj.hSVPS.optResults.dose{end}(:))/1.1]);
  317. alphaMap = interp1([0; 40], [0; 1], doseSlice, 'linear', 1);
  318. alphaMap = reshape(alphaMap, size(doseSlice));
  319. compImage = imblend(doseImage, alphaMap, ctImage, 1);
  320. set(obj.hImage, 'CData', compImage);
  321. end
  322. end
  323. end
  324. %-------------------------------------------------------------------------------
  325. function redraw_contour(obj)
  326. % This function is not called anymore. See redraw_overlay for
  327. % the actual drawing of the contours
  328. slice = obj.hSVPS.POI(obj.zOrien);
  329. disp('Contour!!')
  330. delete(obj.hROIs);
  331. obj.hROIs = [];
  332. hold(obj.hAxis,'on');
  333. for i = 1:numel(obj.hSVPS.Geometry.ROIS)
  334. % skip if display == OFF
  335. if obj.hSVPS.Geometry.ROIS{i}.visible == false
  336. continue;
  337. end
  338. switch obj.zOrien
  339. case 1
  340. curve_indices = find(obj.hSVPS.Geometry.ROIS{i}.Xcurves_slices == slice);
  341. if ~isempty(curve_indices)
  342. for curve_idx = curve_indices
  343. obj.hROIs(end+1) = plot(obj.hAxis, ...
  344. obj.hSVPS.Geometry.ROIS{i}.Xcurves{curve_idx}(:,3), ...
  345. obj.hSVPS.Geometry.ROIS{i}.Xcurves{curve_idx}(:,2), ...
  346. '--', 'LineWidth', 2, 'Color', obj.hSVPS.Geometry.ROIS{i}.color);
  347. end
  348. end
  349. case 2
  350. curve_indices = find(obj.hSVPS.Geometry.ROIS{i}.Ycurves_slices == slice);
  351. if ~isempty(curve_indices)
  352. for curve_idx = curve_indices
  353. obj.hROIs(end+1) = plot(obj.hAxis, ...
  354. obj.hSVPS.Geometry.ROIS{i}.Ycurves{curve_idx}(:,3), ...
  355. obj.hSVPS.Geometry.ROIS{i}.Ycurves{curve_idx}(:,1), ...
  356. '--', 'LineWidth', 2, 'Color', obj.hSVPS.Geometry.ROIS{i}.color);
  357. end
  358. end
  359. case 3
  360. % curve_indices = find(abs(obj.hSVPS.Geometry.ROIS{i}.curvesZ - obj.hSVPS.Geometry.z(slice)) < 1E-2);
  361. curve_indices = find(obj.hSVPS.Geometry.ROIS{i}.Zcurves_slices == slice);
  362. % skip if no curve found on current slice
  363. if ~isempty(curve_indices)
  364. for curve_idx = curve_indices
  365. obj.hROIs(end+1) = plot(obj.hAxis, ...
  366. obj.hSVPS.Geometry.ROIS{i}.curves{curve_idx}(:,1), ...
  367. obj.hSVPS.Geometry.ROIS{i}.curves{curve_idx}(:,2), ...
  368. '--', 'LineWidth', 2, 'Color', obj.hSVPS.Geometry.ROIS{i}.color);
  369. end
  370. end
  371. end
  372. end
  373. hold(obj.hAxis,'off');
  374. end
  375. %-------------------------------------------------------------------------------
  376. function redraw_overlay(obj)
  377. slice = obj.hSVPS.POI(obj.zOrien);
  378. delete(obj.hROIs);
  379. obj.hROIs = [];
  380. hold(obj.hAxis,'on');
  381. for i = 1:numel(obj.hSVPS.Geometry.ROIS)
  382. % skip if display == OFF; first if statement checks if
  383. % 'visible' field has been created yet
  384. % fprintf('ROI #%.0f', i)
  385. if isfield(obj.hSVPS.Geometry.ROIS{i},'visible')==false
  386. continue;
  387. end
  388. if obj.hSVPS.Geometry.ROIS{i}.visible == false
  389. % fprintf(' skipped!\n', i)
  390. continue;
  391. end
  392. % fprintf(' analyzed!\n', i)
  393. testIdxList=obj.hSVPS.Geometry.ROIS{i}.ind;
  394. matSize=size(obj.hSVPS.Geometry.data);
  395. [x,y,z] = ind2sub(matSize, testIdxList);
  396. a=zeros(matSize);
  397. a(testIdxList)=1;
  398. % orthoslice(a, [0,1], 3)
  399. % orthoslice(obj.hSVPS.Geometry.data, [0,500], 3)
  400. % fprintf('ROI #%.0f \n', disp(obj.zOrien))
  401. switch obj.zOrien
  402. case 1
  403. curve_indices = find(x == slice);
  404. if ~isempty(curve_indices)
  405. for curve_idx = curve_indices
  406. canvas=zeros(matSize(2),matSize(3));
  407. idx=sub2ind([matSize(2),matSize(3)], y(curve_indices), z(curve_indices));
  408. canvas(idx)=1;
  409. % orthoslice(obj.hSVPS.Geometry.data, [0,1])
  410. % sum(canvas(:))
  411. B = bwboundaries(canvas);
  412. numContours=length(B);
  413. for contourI=1:numContours
  414. B_I = B{contourI};
  415. clear C
  416. % up-down (+ high)
  417. yshift= obj.hSVPS.Geometry.start(2) - 1.0*obj.hSVPS.Geometry.voxel_size(2);
  418. zshift= obj.hSVPS.Geometry.start(3) - 1.0*obj.hSVPS.Geometry.voxel_size(3);
  419. C(:,1)= yshift + (B_I(:,1))*obj.hSVPS.Geometry.voxel_size(1);
  420. C(:,2)= zshift + (B_I(:,2))*obj.hSVPS.Geometry.voxel_size(3);
  421. obj.hROIs(end+1) = plot(obj.hAxis, ...
  422. C(:,2), C(:,1), ...
  423. '-', 'LineWidth', 2, 'Color', obj.hSVPS.Geometry.ROIS{i}.color);
  424. end
  425. end
  426. end % end of IF
  427. case 2
  428. curve_indices = find(y == slice);
  429. if ~isempty(curve_indices)
  430. for curve_idx = curve_indices
  431. canvas=zeros(matSize(1),matSize(3));
  432. idx=sub2ind([matSize(1),matSize(3)], x(curve_indices), z(curve_indices));
  433. canvas(idx)=1;
  434. % sum(canvas(:))
  435. B = bwboundaries(canvas);
  436. numContours=length(B);
  437. for contourI=1:numContours
  438. B_I = B{contourI};
  439. clear C
  440. % up-down (+ high)
  441. xshift= obj.hSVPS.Geometry.start(1) - 1.0*obj.hSVPS.Geometry.voxel_size(1);
  442. zshift= obj.hSVPS.Geometry.start(3) - 1.0*obj.hSVPS.Geometry.voxel_size(3);
  443. C(:,1)= xshift + (B_I(:,1))*obj.hSVPS.Geometry.voxel_size(1);
  444. C(:,2)= zshift + (B_I(:,2))*obj.hSVPS.Geometry.voxel_size(3);
  445. obj.hROIs(end+1) = plot(obj.hAxis, ...
  446. C(:,2), C(:,1), ...
  447. '-', 'LineWidth', 2, 'Color', obj.hSVPS.Geometry.ROIS{i}.color);
  448. end
  449. end
  450. end % end of IF
  451. case 3
  452. curve_indices = find(z == slice);
  453. if ~isempty(curve_indices)
  454. for curve_idx = curve_indices
  455. canvas=zeros(matSize(1),matSize(2));
  456. idx=sub2ind([matSize(1),matSize(2)], x(curve_indices), y(curve_indices));
  457. canvas(idx)=1;
  458. % sum(canvas(:))
  459. B = bwboundaries(canvas);
  460. numContours=length(B);
  461. for contourI=1:numContours
  462. B_I = B{contourI};
  463. clear C
  464. % up-down (+ high)
  465. yshift= obj.hSVPS.Geometry.start(2) - 1.0*obj.hSVPS.Geometry.voxel_size(2);
  466. xshift= obj.hSVPS.Geometry.start(1) - 1.0*obj.hSVPS.Geometry.voxel_size(1);
  467. C(:,1)= yshift + (B_I(:,1))*obj.hSVPS.Geometry.voxel_size(1);
  468. C(:,2)= xshift + (B_I(:,2))*obj.hSVPS.Geometry.voxel_size(2);
  469. obj.hROIs(end+1) = plot(obj.hAxis, ...
  470. C(:,2), C(:,1), ...
  471. '-', 'LineWidth', 2, 'Color', obj.hSVPS.Geometry.ROIS{i}.color);
  472. end
  473. end
  474. end % end of IF
  475. end
  476. end
  477. hold(obj.hAxis,'off');
  478. end
  479. %-------------------------------------------------------------------------------
  480. end % methods
  481. %===============================================================================
  482. end