Reputation: 102
I'm building a MATLAB GUI using GUIDE for processing medical images (brain tumor segmentation on MRI scans). As a preprocessing step, the program coregisters different scans, which looks like this:
I now want to display crosshairs on both images as a visual check of the coregistration. The crosshairs should be linked to one another, such that they point to the same pixel in both images. Moreover, it should move when hovering over (or clicking on) one of the images. This is what I want to achieve:
Does a build-in MATLAB function exist that can achieve this? Or, if I have to write it myself, how would one tackle this problem?
Upvotes: 1
Views: 329
Reputation: 102
The code below is inspired by the answer of BillBokeey, for which I'm very grateful.
First, do steps 0 and 1 of BillBokeey's solution. Then I put this function in my code:
function getMousePositionOnImage(src, event)
% Fetch the current handles structure
handles = guidata(src);
% Get the coordinate IN THE AXES UNITS OF AXES1 (Here I chose to define them
% as pixels) of the point where the user clicked
cursorPoint1 = get(handles.axes2, 'CurrentPoint');
cursorPoint2 = get(handles.axes3, 'CurrentPoint');
% Get the Position of the first image (We're only interested by the width
% and height of the axes1 object)
Img1Pos = getpixelposition(handles.axes2);
Img2Pos = getpixelposition(handles.axes3);
XLim1 = get(handles.axes2, 'XLim');
YLim1 = get(handles.axes2, 'YLim');
XLim2 = get(handles.axes3, 'XLim');
YLim2 = get(handles.axes3, 'YLim');
% Check if inside
if (cursorPoint1(1)<XLim1(2) && cursorPoint1(2)<YLim1(2) && cursorPoint1(1)>=XLim1(1) && cursorPoint1(1)>=YLim1(1))
Lines1=findobj('Type','line','Parent',handles.axes2);
Lines2=findobj('Type','line','Parent',handles.axes3);
if isempty(Lines1)
% If Lines is empty, we need to create the line objects
line(XLim1,[cursorPoint1(1,2) cursorPoint1(1,2)],'Color','g','Parent',handles.axes2);
line([cursorPoint1(1,1) cursorPoint1(1,1)],YLim1,'Color','g','Parent',handles.axes2);
line(XLim2,[cursorPoint1(1,2) cursorPoint1(1,2)],'Color','g','Parent',handles.axes3);
line([cursorPoint1(1,1) cursorPoint1(1,1)],YLim2,'Color','g','Parent',handles.axes3);
else
% If not, we just update the fields XData and YData of both Lines
Lines1(1).XData=XLim1; % Unnecessary but I'll leave it there for clarity
Lines1(1).YData=[cursorPoint1(1,2) cursorPoint1(1,2)];
Lines1(2).XData=[cursorPoint1(1,1) cursorPoint1(1,1)];
Lines1(2).YData=YLim1; % Unnecessary but I'll leave it there for clarity
Lines2(1).XData=XLim2; % Unnecessary but I'll leave it there for clarity
Lines2(1).YData=[cursorPoint1(1,2) cursorPoint1(1,2)];
Lines2(2).XData=[cursorPoint1(1,1) cursorPoint1(1,1)];
Lines2(2).YData=YLim2; % Unnecessary but I'll leave it there for clarity
end
elseif (cursorPoint2(1)<XLim2(2) && cursorPoint2(2)<YLim2(2) && cursorPoint2(1)>=XLim2(1) && cursorPoint2(1)>=YLim2(1))
Lines1=findobj('Type','line','Parent',handles.axes2);
Lines2=findobj('Type','line','Parent',handles.axes3);
if isempty(Lines2)
% If Lines is empty, we need to create the line objects
line(XLim1,[cursorPoint2(1,2) cursorPoint2(1,2)],'Color','g','Parent',handles.axes2);
line([cursorPoint2(1,1) cursorPoint2(1,1)],YLim1,'Color','g','Parent',handles.axes2);
line(XLim2,[cursorPoint2(1,2) cursorPoint2(1,2)],'Color','g','Parent',handles.axes3);
line([cursorPoint2(1,1) cursorPoint2(1,1)],YLim2,'Color','g','Parent',handles.axes3);
else
% If not, we just update the fields XData and YData of both Lines
Lines1(1).XData=XLim1; % Unnecessary but I'll leave it there for clarity
Lines1(1).YData=[cursorPoint2(1,2) cursorPoint2(1,2)];
Lines1(2).XData=[cursorPoint2(1,1) cursorPoint2(1,1)];
Lines1(2).YData=YLim1; % Unnecessary but I'll leave it there for clarity
Lines2(1).XData=XLim2; % Unnecessary but I'll leave it there for clarity
Lines2(1).YData=[cursorPoint2(1,2) cursorPoint2(1,2)];
Lines2(2).XData=[cursorPoint2(1,1) cursorPoint2(1,1)];
Lines2(2).YData=YLim2; % Unnecessary but I'll leave it there for clarity
end
end
Upvotes: 0
Reputation: 3511
I'll use a Toy GUI in order to show how it works :
Let's first try to make it work for the first image only. What you want to achieve is :
You want to make sure of some details first :
hold on
after both your imshow
calls, otherwise the crossbars will delete your image instead of overlaying on it.In your GUI opening function (here it will be SuperDuperGUI_OpeningFcn
), you want to add a call to :
set(gcf, 'WindowButtonDownFcn', @getMousePositionOnImage);
This will trigger the function getMousePositionOnImage
everytime the user clicks inside your GUI.
Then, you want to add and implement the function getMousePositionOnImage
:
function getMousePositionOnImage(src, event)
% Fetch the current handles structure
handles = guidata(src);
% Get the coordinate IN THE AXES UNITS OF AXES1 (Here I chose to define them
% as pixels) of the point where the user clicked
cursorPoint = get(handles.axes1, 'CurrentPoint')
Still in the getMousePositionOnImage
function :
% Get the Position of the first image (We're only interested by the width
% and height of the axes1 object)
Img1Pos=get(handles.axes1,'Position')
% Check if inside
if(cursorPoint(1,1)<Img1Pos(3)&&cursorPoint(1,2)<Img1Pos(4)&&cursorPoint(1,1)>=0&&cursorPoint(1,2)>=0)
% Do stuff
end
% Check if inside
if(cursorPoint(1,1)<Img1Pos(3)&&cursorPoint(1,2)<Img1Pos(4)&&cursorPoint(1,1)>=0&&cursorPoint(1,2)>=0)
Lines=findobj('Type','line','Parent',handles.axes1);
if isempty(Lines)
% If Lines is empty, we need to create the line objects
line([0 Img1Pos(3)],[cursorPoint(1,2) cursorPoint(1,2)],'Color','g','Parent',handles.axes1);
line([cursorPoint(1,1) cursorPoint(1,1)],[0 Img1Pos(4)],'Color','g','Parent',handles.axes1);
else
% If not, we just update the fields XData and YData of both Lines
Lines(1).XData=[0 Img1Pos(3)]; % Unnecessary but I'll leave it there for clarity
Lines(1).YData=[cursorPoint(1,2) cursorPoint(1,2)];
Lines(2).XData=[cursorPoint(1,1) cursorPoint(1,1)];
Lines(2).YData=[0 Img1Pos(4)]; % Unnecessary but I'll leave it there for clarity
end
end
Now I'll let you do the last part, which involves linking the two crossbars. Instead of only checking if the click was on first image, you'll check both separately. Then, if it is in one of the image, you'll update both crossbar to the position of the click in the right axes object
Upvotes: 1