Matlab:在 GUI 中的两个图像上同时显示十字准线

Matlab: display crosshairs simultaneously on two images in GUI

我正在使用 GUIDE 构建一个 MATLAB GUI 来处理医学图像(MRI 扫描中的脑肿瘤分割)。作为预处理步骤,程序会注册不同的扫描,如下所示:

我现在想在两张图片上都显示十字准线,以目视检查配准。十字准线应该相互链接,以便它们指向两个图像中的相同像素。此外,当将鼠标悬停在(或单击)其中一张图像上时,它应该会移动。这就是我想要实现的目标:

是否存在可以实现此目的的内置 MATLAB 函数?或者,如果我必须自己编写,将如何解决这个问题?

我将使用 Toy GUI 来展示它的工作原理:

让我们首先尝试让它仅适用于第一张图片。你想要实现的是:

  • 当用户点击图形时,我们想要获得鼠标点击的位置。
  • 然后我们要检查点击是否位于第一张图片内
  • 如果是,我们要更新横杆的位置,这将由叠加在图像上并在所选点交叉的两条线表示。

第 0 步:初始设置

你要先确认一些细节:

  • 确保在两次 imshow 调用之后添加对 hold on 的调用,否则交叉栏将删除您的图像而不是叠加在图像上。

第一步:获取鼠标点击的位置

在您的 GUI 打开函数中(此处为 SuperDuperGUI_OpeningFcn),您想添加对 :

的调用
set(gcf, 'WindowButtonDownFcn', @getMousePositionOnImage);

这将在用户每次在您的 GUI 中单击时触发函数 getMousePositionOnImage

然后,您要添加并实现函数 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')

第 2 步:检查鼠标点击是否在第一张图片内

仍在getMousePositionOnImage函数中:

% 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

第 3 步:如果点击在第一张图片内,更新横杆的位置

% 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

结果:

现在我将让您完成最后一部分,其中涉及连接两个横杆。您将单独检查两者,而不是只检查点击是否在第一张图片上。然后,如果它在其中一张图片中,您将更新 both crossbar 到右轴对象中点击的位置

编辑

下面的代码是受 BillBokeey 的回答启发而来的,非常感谢。

首先,执行 BillBokeey 解决方案的第 0 步和第 1 步。然后我把这个函数放在我的代码中:

    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