diff --git a/ShoulderCase/@RotatorCuff/plot.m b/ShoulderCase/@RotatorCuff/plot.m index 28b47f6..7644856 100644 --- a/ShoulderCase/@RotatorCuff/plot.m +++ b/ShoulderCase/@RotatorCuff/plot.m @@ -1,46 +1,54 @@ function output = plot(obj,varargin) if nargin == 2 error('Provide the slice name and the mask name or provide nothing (auto slice and segmentations will be plotted).') elseif nargin == 3 sliceName = varargin{1} + "_ForSegmentation.png"; - maskName = varargin{2} + "_Mask.png"; + maskName = varargin{2} + "_Segmentation.png"; else sliceName = "rotatorCuffMatthieu_ForSegmentation.png"; - maskName = "autoMatthieu_Mask.png"; + maskName = "autoMatthieu_Segmentation.png"; end rotatorCuffCrossSection = imread(fullfile(obj.dataSlicesPath,sliceName)); leg = {}; plotHandle(1) = imshow(rotatorCuffCrossSection); hold on muscleColor = containers.Map; muscleColor("SC") = "b"; muscleColor("SS") = "r"; muscleColor("IS") = "g"; muscleColor("TM") = "y"; for muscleName = ["SC", "SS", "IS", "TM"] try - segmentedImage = imread(fullfile(obj.(muscleName).dataMaskPath,maskName)); - catch - continue + plotHandle(end+1) = plotMuscleContour(obj.(muscleName), muscleColor); + leg{end + 1} = muscleName; end - - if (max(segmentedImage,[],'all') > 0) % if segmentation result is not empty - leg{end+1} = muscleName; - [~,plotHandle(end+1)] = contour(segmentedImage,'color',muscleColor(muscleName)); - end - try - centroid = obj.(muscleName).getCentroid().indices; - leg{end+1} = muscleName + " centroid"; - plotHandle(end+1) = scatter(centroid(1), centroid(2), 50, "o",... - "MarkerEdgeColor", muscleColor(muscleName),... - "MarkerFaceColor", muscleColor(muscleName)); + plotHandle(end+1) = plotMuscleCentroid(obj.(muscleName), muscleColor); + leg{end + 1} = muscleName + " centroid"; end end plotHandle(end+1) = legend(leg, "Location", "bestoutside"); output = plotHandle; end + +function output = plotMuscleContour(muscle, muscleColor) + muscleContour = muscle.loadMask("Contour"); + [contourY, contourX] = find(muscleContour); + output = scatter(contourX, contourY,... + 1, ".",... + "MarkerEdgeColor", muscleColor(muscle.name),... + "MarkerFaceColor", muscleColor(muscle.name)); +end + +function output = plotMuscleCentroid(muscle, muscleColor) + muscleCentroid = muscle.loadMask("Centroid"); + [centroidY, centroidX] = find(muscleCentroid); + output = scatter(centroidX, centroidY,... + 50, "o",... + "MarkerEdgeColor", muscleColor(muscle.name),... + "MarkerFaceColor", muscleColor(muscle.name)); +end diff --git a/ShoulderCase/@RotatorCuff/plot3.m b/ShoulderCase/@RotatorCuff/plot3.m index 85942c3..fee6543 100644 --- a/ShoulderCase/@RotatorCuff/plot3.m +++ b/ShoulderCase/@RotatorCuff/plot3.m @@ -1,38 +1,38 @@ function output = plot3(obj) muscleColor = containers.Map; muscleColor("SC") = "b"; muscleColor("SS") = "r"; muscleColor("IS") = "g"; muscleColor("TM") = "y"; plotHandle = []; for muscleName = ["SC", "SS", "IS", "TM"] try % Plot segmentations - centroidPoint = obj.(muscleName).getCentroid().coordinates; - contourPoints = obj.(muscleName).getContour().coordinates; + centroidPoint = obj.(muscleName).centroid; plotHandle(end+1) = scatter3(centroidPoint(:,1), centroidPoint(:,2), centroidPoint(:,3),... "MarkerEdgeColor", muscleColor(muscleName),... "MarkerFaceColor", muscleColor(muscleName)); hold on + contourPoints = obj.(muscleName).getMaskCoordinates("Contour"); plotHandle(end+1) = scatter3(contourPoints(:,1), contourPoints(:,2), contourPoints(:,3),... "MarkerEdgeColor", muscleColor(muscleName)); - % Plot muscle insertion - humeralInsertion = obj.(muscleName).insertions; + % Plot muscle contact point with humeral head + humeralContactPoint = obj.(muscleName).forceApplicationPoint; plotHandle(end+1) = plot(... - Vector(centroidPoint, humeralInsertion),... + Vector(centroidPoint, humeralContactPoint),... "Color", muscleColor(muscleName),... "LineWidth", 2); plotHandle(end+1) = scatter3(... - humeralInsertion(1),... - humeralInsertion(2),... - humeralInsertion(3),... + humeralContactPoint(1),... + humeralContactPoint(2),... + humeralContactPoint(3),... "MarkerFaceColor", muscleColor(muscleName),... "MarkerEdgeColor", "magenta"); end end output = plotHandle; end \ No newline at end of file diff --git a/ShoulderCase/@ShoulderCase/plot.m b/ShoulderCase/@ShoulderCase/plot.m index d2a8078..8c3f9fd 100644 --- a/ShoulderCase/@ShoulderCase/plot.m +++ b/ShoulderCase/@ShoulderCase/plot.m @@ -1,3 +1,3 @@ function output = plot(obj,varargin) - output = ShoulderCasePlotter(obj); + output = ShoulderCasePlotter(obj, varargin{:}); end \ No newline at end of file diff --git a/ShoulderCase/@ShoulderCase/plotManualAutoDifferences.m b/ShoulderCase/@ShoulderCase/plotManualAutoDifferences.m index b646aec..11b57d4 100644 --- a/ShoulderCase/@ShoulderCase/plotManualAutoDifferences.m +++ b/ShoulderCase/@ShoulderCase/plotManualAutoDifferences.m @@ -1,70 +1,70 @@ -function output = plotManualAutoDifference(obj,normLimitForOutliers) +function output = plotManualAutoDifference(obj, shoulderSide, normLimitForOutliers) % Plot the vectors between auto and manual points. % % input: normLimitForOutliers % % The vector is green if below the value given to normLimitForOutliers, % otherwise it is red. % initialisation - manual = obj.shoulderManual; + manual = obj.shoulders.(shoulderSide).manual; manualPoints = []; - auto = obj.shoulderAuto; + auto = obj.shoulders.(shoulderSide).auto; autoPoints = []; % find maximum number of common groove points minGroovePoints = min(size(manual.scapula.groove),size(auto.scapula.groove)); if (minGroovePoints > 0) manualPoints = [manual.scapula.groove(1:minGroovePoints,:)]; autoPoints = [auto.scapula.groove(1:minGroovePoints,:)]; end % create points arrays manualPoints = [manualPoints;... manual.scapula.angulusInferior;... manual.scapula.trigonumSpinae;... manual.scapula.processusCoracoideus;... manual.scapula.acromioClavicular;... manual.scapula.angulusAcromialis;... manual.scapula.spinoGlenoidNotch;... manual.scapula.coordSys.origin;... manual.scapula.glenoid.center;... ]; autoPoints = [autoPoints;... auto.scapula.angulusInferior;... auto.scapula.trigonumSpinae;... auto.scapula.processusCoracoideus;... auto.scapula.acromioClavicular;... auto.scapula.angulusAcromialis;... auto.scapula.spinoGlenoidNotch;... auto.scapula.coordSys.origin;... auto.scapula.glenoid.center;... ]; % measure norm difference manualAutoDifference = vecnorm(autoPoints-manualPoints,2,2); % plot the difference vectors for i = 1:size(manualAutoDifference,1) if (manualAutoDifference(i) > normLimitForOutliers) % outlier color = 'red'; else % valid point color = 'green'; end differenceLine = [manualPoints(i,:);... autoPoints(i,:)]; plotHandle(i) = plot3(differenceLine(:,1),differenceLine(:,2),differenceLine(:,3),... ':x',... 'Color',color,... 'LineWidth',3); hold on end output = plotHandle; end \ No newline at end of file diff --git a/ShoulderCase/@ShoulderCasePlotter/ShoulderCasePlotter.m b/ShoulderCase/@ShoulderCasePlotter/ShoulderCasePlotter.m index 0f3e615..e4c231c 100644 --- a/ShoulderCase/@ShoulderCasePlotter/ShoulderCasePlotter.m +++ b/ShoulderCase/@ShoulderCasePlotter/ShoulderCasePlotter.m @@ -1,61 +1,65 @@ classdef ShoulderCasePlotter < handle % Create a figure with ShoulderCase's plots % and buttons to toggle the data visualisation. % % Used by the ShoulderCase.plot() method. properties (Access = private) SCase + shoulderSide = "right" fig axesHandle axesLink axesCamera lightHandle plotHandle buttonHandle options = {'landmarks',... 'scapula surface',... 'glenoid surface',... 'coordinate system',... 'centered coordinate system',... 'rotator cuff',... 'difference'}; end methods (Access = ?ShoulderCase) - function obj = ShoulderCasePlotter(SCase) + function obj = ShoulderCasePlotter(SCase, varargin) obj.SCase = SCase; + if nargin > 1 + obj.shoulderSide = varargin{1}; + end obj.fig = figure('Name',SCase.id,... 'NumberTitle','off',... 'units','normalized',... 'outerposition',[0 0 1 1]); obj.axesHandle = containers.Map; obj.plotHandle = {}; obj.buttonHandle = {}; obj.initializeAxes(); obj.configCamera(); obj.plot(); obj.linkAxes(); obj.initializeButton(); axes(obj.axesHandle('scapula')); end end methods (Access = private) initializeAxes(obj); initializeButton(obj); plot(obj); configCamera(obj); linkAxes(obj); end end \ No newline at end of file diff --git a/ShoulderCase/@ShoulderCasePlotter/configCamera.m b/ShoulderCase/@ShoulderCasePlotter/configCamera.m index 8b79f9b..3b4aa44 100644 --- a/ShoulderCase/@ShoulderCasePlotter/configCamera.m +++ b/ShoulderCase/@ShoulderCasePlotter/configCamera.m @@ -1,35 +1,35 @@ function configCamera(obj) try - view(obj.axesHandle('scapula'),obj.SCase.shoulderManual.scapula.coordSys.ML); + view(obj.axesHandle('scapula'),obj.SCase.shoulders.(obj.shoulderSide).manual.scapula.coordSys.ML); catch - view(obj.axesHandle('scapula'),obj.SCase.shoulderAuto.scapula.coordSys.ML); + view(obj.axesHandle('scapula'),obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.coordSys.ML); end set(obj.axesHandle('scapula'),'CameraViewAngle',... 0.7*get(obj.axesHandle('scapula'),'CameraViewAngle')); try - set(obj.axesHandle('scapula'),'CameraUpVector',obj.SCase.shoulderManual.scapula.coordSys.IS); + set(obj.axesHandle('scapula'),'CameraUpVector',obj.SCase.shoulders.(obj.shoulderSide).manual.scapula.coordSys.IS); catch - set(obj.axesHandle('scapula'),'CameraUpVector',obj.SCase.shoulderAuto.scapula.coordSys.IS); + set(obj.axesHandle('scapula'),'CameraUpVector',obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.coordSys.IS); end set(obj.axesHandle('centered coordinate system'),'CameraViewAngle',... 0.6*get(obj.axesHandle('centered coordinate system'),'CameraViewAngle')); set(obj.axesHandle('centered coordinate system'),'CameraTarget',[0 0 0]); cameratoolbar('SetMode','orbit'); cameratoolbar('SetCoordSys','none'); axes(obj.axesHandle('scapula')); - if not(obj.SCase.shoulderAuto.scapula.coordSys.isEmpty) - coordSys = obj.SCase.shoulderAuto.scapula.coordSys; - elseif not(obj.SCase.shoulderManual.scapula.coordSys.isEmpty) - coordSys = obj.SCase.shoulderManual.scapula.coordSys; + if not(obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.coordSys.isEmpty) + coordSys = obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.coordSys; + elseif not(obj.SCase.shoulders.(obj.shoulderSide).manual.scapula.coordSys.isEmpty) + coordSys = obj.SCase.shoulders.(obj.shoulderSide).manual.scapula.coordSys; else return; end obj.lightHandle(1) = light('Position',coordSys.origin + coordSys.PA*200 - coordSys.IS*100 - coordSys.ML*100); obj.lightHandle(2) = light('Position',coordSys.origin - coordSys.PA*200 - coordSys.IS*100 - coordSys.ML*100); obj.lightHandle(3) = light('Position',coordSys.origin + coordSys.IS*200); obj.lightHandle(4) = light('Position',coordSys.origin + coordSys.ML*200); end \ No newline at end of file diff --git a/ShoulderCase/@ShoulderCasePlotter/initializeButton.m b/ShoulderCase/@ShoulderCasePlotter/initializeButton.m index 5e167f3..af3b452 100644 --- a/ShoulderCase/@ShoulderCasePlotter/initializeButton.m +++ b/ShoulderCase/@ShoulderCasePlotter/initializeButton.m @@ -1,111 +1,111 @@ function initializeButton(obj) autoPanel = uipanel(obj.fig,... 'Title','Auto (in blue)',... 'units','pixels',... 'Position',[10 340 190 170]); for i = 1:length(obj.options)-1 try obj.buttonHandle.auto(i) = uicontrol(autoPanel,... 'Style','checkbox',... 'Value',1,... 'String',obj.options{i},... 'position',[10,150-i*20,150,20],... 'Callback',{@setDataVisibility,obj.plotHandle.auto(obj.options{i})}); end end obj.buttonHandle.auto(i+1) = uicontrol(autoPanel,... 'Style','pushbutton',... 'String','check/uncheck all other buttons',... 'position',[10,150-(i+1)*20,170,20],... 'Callback',{@checkUncheckAllButtons,obj.buttonHandle.auto,obj.plotHandle.auto}); manualPanel = uipanel(obj.fig,... 'Title','Manual (in red)',... 'units','pixels',... 'Position',[10 160 190 170]); for i = 1:length(obj.options)-1 try obj.buttonHandle.manual(i) = uicontrol(manualPanel,... 'Style','checkbox',... 'Value',1,... 'String',obj.options{i},... 'position',[10,150-i*20,150,20],... 'Callback',{@setDataVisibility,obj.plotHandle.manual(obj.options{i})}); end end obj.buttonHandle.manual(i+1) = uicontrol(manualPanel,... 'Style','pushbutton',... 'String','check/uncheck all other buttons',... 'position',[10,150-(i+1)*20,170,20],... 'Callback',{@checkUncheckAllButtons,obj.buttonHandle.manual,obj.plotHandle.manual}); try obj.buttonHandle.difference = uicontrol(obj.fig,... 'Style','checkbox',... 'Value',1,... 'String','auto/manual difference',... 'position',[20,130,150,20],... 'Callback',{@setDataVisibility,obj.plotHandle.difference('difference')}); end cameraPanel = uipanel(obj.fig,... 'Title','Camera',... 'units','pixels',... 'Position',[10 60 220 60]); obj.buttonHandle.camera(1) = uicontrol(cameraPanel,... 'Style','pushbutton',... 'String','ML view',... 'position',[10,10,60,30],... 'Callback',{@setCameraView,obj.axesHandle,... - obj.SCase.shoulderAuto.scapula.coordSys.ML,... - obj.SCase.shoulderAuto.scapula.coordSys.IS}); + obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.coordSys.ML,... + obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.coordSys.IS}); obj.buttonHandle.camera(2) = uicontrol(cameraPanel,... 'Style','pushbutton',... 'String','IS view',... 'position',[80,10,60,30],... 'Callback',{@setCameraView,obj.axesHandle,... - obj.SCase.shoulderAuto.scapula.coordSys.IS,... - -obj.SCase.shoulderAuto.scapula.coordSys.ML}); + obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.coordSys.IS,... + -obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.coordSys.ML}); obj.buttonHandle.camera(3) = uicontrol(cameraPanel,... 'Style','pushbutton',... 'String','PA view',... 'position',[150,10,60,30],... 'Callback',{@setCameraView,obj.axesHandle,... - obj.SCase.shoulderAuto.scapula.coordSys.PA,... - obj.SCase.shoulderAuto.scapula.coordSys.IS}); + obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.coordSys.PA,... + obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.coordSys.IS}); end function setDataVisibility(button,event,dataHandle) for i = 1:length(dataHandle) set(dataHandle(i),'Visible',logical(button.Value)); end end function checkUncheckAllButtons(button,event,buttonHandle,plotHandle) buttonState = logical([buttonHandle(:).Value]); if all(buttonState) newValue = false; else newValue = true; end for i = 1:length(buttonHandle) set(buttonHandle(i),'Value',newValue); setDataVisibility(buttonHandle(i),[],plotHandle(buttonHandle(i).String)); end end function setCameraView(button,event,axesHandle,viewAxis,upAxis) axes(axesHandle('scapula')); target = get(gca,'CameraTarget'); view(viewAxis); set(axesHandle('scapula'),'CameraUpVector',upAxis); set(axesHandle('scapula'),'CameraTarget',target); end diff --git a/ShoulderCase/@ShoulderCasePlotter/plot.m b/ShoulderCase/@ShoulderCasePlotter/plot.m index 5720b7d..909c2aa 100644 --- a/ShoulderCase/@ShoulderCasePlotter/plot.m +++ b/ShoulderCase/@ShoulderCasePlotter/plot.m @@ -1,100 +1,100 @@ function plot(obj) % set method colors autoColors = {'b','cyan'}; manualColors = {'#D95319','#ffcc00'}; % plot scapula data axes(obj.axesHandle('scapula')); hold on; grid on axis vis3d auto = containers.Map; try - auto('landmarks') = obj.SCase.shoulderAuto.scapula.plotLandmarks(autoColors{2},autoColors{2}); + auto('landmarks') = obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.plotLandmarks(autoColors{2},autoColors{2}); end try - auto('scapula surface') = obj.SCase.shoulderAuto.scapula.plotSurface(autoColors{1},autoColors{1}); + auto('scapula surface') = obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.plotSurface(autoColors{1},autoColors{1}); end try - auto('glenoid surface') = obj.SCase.shoulderAuto.scapula.glenoid.plot(); + auto('glenoid surface') = obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.glenoid.plot(); end try - auto('coordinate system') = obj.SCase.shoulderAuto.scapula.coordSys.plot(autoColors{1},false); + auto('coordinate system') = obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.coordSys.plot(autoColors{1},false); end manual = containers.Map; try - manual('landmarks') = obj.SCase.shoulderManual.scapula.plotLandmarks(manualColors{2},manualColors{2}); + manual('landmarks') = obj.SCase.shoulders.(obj.shoulderSide).manual.scapula.plotLandmarks(manualColors{2},manualColors{2}); end try - manual('scapula surface') = obj.SCase.shoulderManual.scapula.plotSurface(manualColors{1},manualColors{1}); + manual('scapula surface') = obj.SCase.shoulders.(obj.shoulderSide).manual.scapula.plotSurface(manualColors{1},manualColors{1}); end try - manual('glenoid surface') = obj.SCase.shoulderManual.scapula.glenoid.plot(); + manual('glenoid surface') = obj.SCase.shoulders.(obj.shoulderSide).manual.scapula.glenoid.plot(); end try - manual('coordinate system') = obj.SCase.shoulderManual.scapula.coordSys.plot(manualColors{1},false); + manual('coordinate system') = obj.SCase.shoulders.(obj.shoulderSide).manual.scapula.coordSys.plot(manualColors{1},false); end outliersNormLimit = 10; difference = containers.Map; try - difference('difference') = obj.SCase.plotManualAutoDifferences(outliersNormLimit); + difference('difference') = obj.SCase.plotManualAutoDifferences(obj.shoulderSide, outliersNormLimit); end % plot centered coordinate system axes(obj.axesHandle('centered coordinate system')); hold on; grid on axis vis3d try - auto('centered coordinate system') = obj.SCase.shoulderAuto.scapula.coordSys.plot(autoColors{1},true); + auto('centered coordinate system') = obj.SCase.shoulders.(obj.shoulderSide).auto.scapula.coordSys.plot(autoColors{1},true); end try - manual('centered coordinate system') = obj.SCase.shoulderManual.scapula.coordSys.plot(manualColors{1},true); + manual('centered coordinate system') = obj.SCase.shoulders.(obj.shoulderSide).manual.scapula.coordSys.plot(manualColors{1},true); end % plot muscles segmentation axes(obj.axesHandle('auto muscles')); title('Auto segmentation with auto landmarks'); hold on; try - auto('rotator cuff') = obj.SCase.shoulderAuto.rotatorCuff.plot(); + auto('rotator cuff') = obj.SCase.shoulders.(obj.shoulderSide).auto.rotatorCuff.plot(); end axes(obj.axesHandle('scapula')) try - auto("rotator cuff") = [auto("rotator cuff") obj.SCase.shoulderAuto.rotatorCuff.plot3()]; + auto("rotator cuff") = [auto("rotator cuff") obj.SCase.shoulders.(obj.shoulderSide).auto.rotatorCuff.plot3()]; end try - auto("rotator cuff") = [auto("rotator cuff") obj.SCase.shoulderAuto.humerus.plot()]; + auto("rotator cuff") = [auto("rotator cuff") obj.SCase.shoulders.(obj.shoulderSide).auto.humerus.plot()]; end axes(obj.axesHandle('manual muscles')); title('Auto segmentation with manual landmarks'); hold on; try - manual('rotator cuff') = obj.SCase.shoulderManual.rotatorCuff.plot(); + manual('rotator cuff') = obj.SCase.shoulders.(obj.shoulderSide).manual.rotatorCuff.plot(); end axes(obj.axesHandle('scapula')) try - manual("rotator cuff") = [manual("rotator cuff") obj.SCase.shoulderManual.rotatorCuff.plot3()]; + manual("rotator cuff") = [manual("rotator cuff") obj.SCase.shoulders.(obj.shoulderSide).manual.rotatorCuff.plot3()]; end try - manual("rotator cuff") = [manual("rotator cuff") obj.SCase.shoulderManual.humerus.plot()]; + manual("rotator cuff") = [manual("rotator cuff") obj.SCase.shoulders.(obj.shoulderSide).manual.humerus.plot()]; end % store the data handles obj.plotHandle.auto = auto; obj.plotHandle.manual = manual; obj.plotHandle.difference = difference; end \ No newline at end of file diff --git a/plotSCase.m b/plotSCase.m index b0422f0..d360874 100755 --- a/plotSCase.m +++ b/plotSCase.m @@ -1,20 +1,20 @@ -function [output,varargout] = plotSCase(SCaseID) +function [output,varargout] = plotSCase(SCaseID, varargin) % Call a ShoulderCase.plot function % % Inputs: id char of shoulder case (e.g. 'P315') % % Output: Corresponding ShoulderCasePlotter object % % Example: SCasePlotter = plotSCase('P315'); % % Author: Alexandre Terrier, EPFL-LBO % Matthieu Boubat, EPFL-LBO % Creation date: 2018-07-01 % Revision date: 2020-08-10 addpath(genpath('ShoulderCase')); SCase = loadSCase(SCaseID); - SCasePlotter = plot(SCase); + SCasePlotter = plot(SCase, varargin{:}); output = SCasePlotter; end