sdbonte
sdbonte

Reputation: 102

Matlab: display crosshairs simultaneously on two images in GUI

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:

enter image description here

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:

enter image description here

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

Answers (2)

sdbonte
sdbonte

Reputation: 102

Edit

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

BillBokeey
BillBokeey

Reputation: 3511

I'll use a Toy GUI in order to show how it works :

enter image description here

Let's first try to make it work for the first image only. What you want to achieve is :

  • When the user clicks on the figure, we want to have access to the location of the mouse click.
  • Then we want to check if the click was located inside the first image
  • If it is, we want to update the position of the crossbar, which will be represented by two lines overlayed on the image and crossing at the selected point.

Step 0 : Initial settings

You want to make sure of some details first :

  • Make sure you add a call to hold on after both your imshow calls, otherwise the crossbars will delete your image instead of overlaying on it.

Step 1 : Getting the position of the mouse click

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')

Step 2 : Check if the mouse click is inside the first image

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

Step 3 : If the click was inside the first image, update the position of the crossbar

% 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

Result :

enter image description here

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

Related Questions