限制ROI移动的方法有哪些

What are the methods to restrict ROI movement

我想允许用户更改椭圆形 ROI 的直径或具有固定轴比率的注释(对于圆,比率为 1:1)。此外,椭圆形应仅围绕中心打开,但不允许侧向移动。

目前,我有一个脚本可以读取 ROI 并在用户偏离形状或其中心位置时进行更正。然而,它看起来相当烦人和令人困惑,例如当圆圈变成椭圆形然后又弹回到圆形时。我希望命令允许例如调整大小(使用固定轴比率的选项)但限制(横向)移动。

非常欢迎任何建议。

这道题是more general question here的具体应用。


诀窍是只要 ROI 的大小发生变化就采取行动,然后将其替换为适当的限制。这是通过附加 "listener" 方法完成的 当 ROI 改变时调用。 有两种方法可以为 ROI 做到这一点:

1) 将侦听器附加到 ROI 所在的 imageDisplay

// EXAMPLE using ImageDisplay listener
// This will handle ALL ROIs on the display, so one would like to 
// "filter" specific ROIs. f.e. by using the ROI property "Name".
class CCircleRestrict
{
    number x0,y0
    object Init( object self, number cx, number cy ){ x0 = cx; y0 = cy; return self; }
    void OnRestrict( object self, number e_fl, ImageDisplay idisp, number r_fl, number r_fl2, ROI theROI )
    {
        if ( theROI.ROIGetName() != "special" ) return; // Skip, if it isn't "our" ROI
        if ( !theROI.ROIIsOval() ) return;  // Skip, if it isn't an oval ROI
        // get size of ROI ( as currently dragged by user )
        number t, l, b, r
        theROI.ROIGetOval( t, l, b, r )
        number radius = max( b - t, r - l ) / 2
        // Re-Set the ROI centered on x0/y0 with the new radius
        theROI.ROISetOval( y0 - radius, x0 - radius, y0 + radius, x0 + radius )
    }
}

// Main script "attaches" the display listener to 
void main()
{
    // Create and show test image 
    number size = 512
    number r1 = 20
    number r2 = 20
    number off = 100
    image test := realImage( "Test", 4, size, size )
    test.ShowImage()
    imageDisplay disp = test.ImageGetImageDisplay(0)
    // Add two oval ROIs, name one of them "special" for identification
    ROI specialROI = NewROI()
    specialROI.ROISetName( "special" )
    specialROI.ROISetOval( size/2 - r1, size/2 - r1, size/2 + r1, size/2 + r1 )
    specialROI.ROISetVolatile(0)
    specialROI.ROISetColor(0.1,0.9,0.1)
    disp.ImageDisplayAddROI(specialROI)
    ROI otherROI = NewROI()
    otherROI.ROISetOval( off + size/2 - r2, off +  size/2 - r2, off +  size/2 + r2, off + size/2 + r2 )
    otherROI.ROISetVolatile(0)
    disp.ImageDisplayAddROI(otherROI)
    // Create listener object and attach listener to display
    object dispListener = Alloc(CCircleRestrict).Init( size/2, size/2 )
    disp.ImageDisplayAddEventListener( dispListener, "roi_property_changed:OnRestrict" )
}

EGUPerformActionWithAllShownImages( "Delete" )
main()
EGUPerformActionWithAllShownImages( "Arrange" )

2) 将侦听器附加到 ROI 对象本身

// EXAMPLE using ROI listener
// This will handle changes a specific ROI, regardless of the display(s) it is on
class CCircleRestrict
{
    number x0,y0
    object Init( object self, number cx, number cy ){ x0 = cx; y0 = cy; return self; }
    void OnRestrict( object self, ROI theROI )
    {
        if ( !theROI.ROIIsOval() ) return;  // Skip, if it isn't an oval ROI
        // get size of ROI ( as currently dragged by user )
        number t, l, b, r
        theROI.ROIGetOval( t, l, b, r )
        number radius = max( b - t, r - l ) / 2
        // Re-Set the ROI centered on x0/y0 with the new radius
        theROI.ROISetOval( y0 - radius, x0 - radius, y0 + radius, x0 + radius )
    }
}

// Main script "attaches" the listener to the ROI
void main()
{
    // Create and show test image 
    number size = 512
    number r1 = 20
    number r2 = 20
    number off = 100
    image test := realImage( "Test", 4, size, size )
    test.ShowImage()
    imageDisplay disp = test.ImageGetImageDisplay(0)
    // Add two oval ROIs
    ROI specialROI = NewROI()
    specialROI.ROISetOval( size/2 - r1, size/2 - r1, size/2 + r1, size/2 + r1 )
    specialROI.ROISetVolatile(0)
    specialROI.ROISetColor(0.1,0.9,0.1)
    disp.ImageDisplayAddROI(specialROI)
    ROI otherROI = NewROI()
    otherROI.ROISetOval( off + size/2 - r2, off +  size/2 - r2, off +  size/2 + r2, off + size/2 + r2 )
    otherROI.ROISetVolatile(0)
    disp.ImageDisplayAddROI(otherROI)
    // Create listener object and attach listener to specific ROI
    object roiListener = Alloc(CCircleRestrict).Init( size/2, size/2 )
    ConnectObject( specialROI.ROIGetID(), "changed", "EventID_Name", roiListener, "OnRestrict" )
}

EGUPerformActionWithAllShownImages( "Delete" )
main()
EGUPerformActionWithAllShownImages( "Arrange" )

以上两个示例都将椭圆形 ROI 限制为圆形,但可以直接更改它以实现椭圆形的特定纵横比。

However, it is important to consider that the "newly set" and adjusted ROI will itself again trigger the listener. It has to be ensured that no infinite loop is created in this way, i.e. the triggering of the method for the second time must not result in new restrictions.

长宽比为 1:2 的椭圆形 ROI 的一个简单示例将使用限制方法,如下所示:

void OnRestrict( object self, ROI theROI )
{
    ar = 2
    if ( !theROI.ROIIsOval() ) return;  // Skip, if it isn't an oval ROI
    // get size of ROI ( as currently dragged by user )
    number t, l, b, r
    theROI.ROIGetOval( t, l, b, r )
    number w = r - l
    number h = b - t
    number newW = max( W, AR*H )
    number newH = newW/AR
    // Re-Set the ROI centered on x0/y0 with the new radius
    theROI.ROISetOval( y0 - newH/2, x0 - newW/2, y0 + newH/2, x0 + newW/2 )
}

上面的答案指定了要求的限制类型,但为了完整起见,还应该提到有一些 ROI 属性 在这种情况下可能很有用。

来自 F1 帮助文档:

但是,moveable 属性 取代了 resizable 属性,即如果您不能移动,您无法调整大小。