Search code examples
image-processingmaskroidm-script

How can I get information from circular ROI using dm script?


After making a circular ROI in an image, how can I get the information (average, standar deviation, variance) from that image region using script?

Can I link the position in the ciruclar ROI with original image?


Solution

  • This task is unfortunately not as straight forward and easy as one would hope.

    While scripting supports a convenient shortcut to restrict image operations to rectangular ROIs ( using the img[] notation ), there is nothing like that for irregular ROIs.

    In such a case, one has to manually create a binary mask of a ROI and perform the wanted operations manually. The example script at the bottom of this post shows how the average value of an irregular ROI may be computed.

    • CreateImageWithROI() Creates a test image with two ROIs on it
    • GetFirstIrregularROIOfImage() just returns the first found, irregular ROI of an image
    • GetROIMean() is the actual example

    The command ROIAddToMask() is used to create the mask. Note, that there is also a similar command which would perform the action with all ROIs of an image display at once: ImageDisplayAccumulateROIsToMask()

    Irregular masking

    So far, so good.

    However, it turns out that the newly introduced Circular ROIs do not yet support the mask-creation commands correctly (Tested with GMS 3.1).

    Instead, they always use the bounding rectangle of the ROI:

    Creating mask from ROIs

    It is therefore necessary to go even one step back and read the ROI's coordinates to create a mask from it manually. Get the ROI's bounding-box and create a mask using an icol and irow expression for an ellipse. In the example below:

    • GetFirstOvalROIOfImage() just returns the first found, oval ROI of an image
    • MyAddOvalROIToMask() is the manual mask creation for oval ROIs

    Final example


    Example code:

    image CreateImageWithROI()
    {
        // Create and show image
        number sx = 256, sy = 256
        image img := RealImage( "Image", 4, sx, sy )
        img = sin( 0.1 * iradius ) * cos( 7 * itheta )
        img.ShowImage()
        
        // Create an irregular, closed ROI
        ROI myIrRoi = NewROI()
        myIrRoi.ROIAddVertex( 0.3 * sx, 0.1 * sy )
        myIrRoi.ROIAddVertex( 0.7 * sx, 0.2 * sy )
        myIrRoi.ROIAddVertex( 0.5 * sx, 0.6 * sy )
        myIrRoi.ROIAddVertex( 0.1 * sx, 0.8 * sy )
        myIrRoi.ROISetIsClosed(1)
        myIRRoi.ROISetVolatile(0)
        
        // Create an oval ROI
        ROI myOvalROI = NewROI()
        myOvalROI.ROISetOval( 0.7 * sy, 0.7 * sx, 0.9 * sy, 0.8 * sx )
        myOvalROI.ROISetVolatile(0)
        
        // AddROIs
        imageDisplay disp = img.ImageGetImageDisplay( 0 )
        disp.ImageDisplayAddROI( myIRRoi )
        disp.ImageDisplayAddROI( myOvalROI )
        
        return img
    }
    
    ROI GetFirstIrregularROIOfImage( image img )
    {
        if ( img.ImageIsValid() ) 
        {
            if ( 0 != img.ImageCountImageDisplays() ) 
            {
                imageDisplay disp = img.ImageGetImageDisplay( 0 )
                number nRois = disp.ImageDisplayCountROIs()
                for ( number i = 0; i < nRois; i++ )
                {
                    ROI testROI = disp.ImageDisplayGetRoi( i )
                    number isIrregularClosed = 1
                    isIrregularClosed *= testROI.ROIIsClosed();
                    isIrregularClosed *= !testROI.ROIIsOval();
                    isIrregularClosed *= !testROI.ROIIsRectangle();
                    isIrregularClosed *= ( 2 < testROI.ROICountVertices());
                    if ( isIrregularClosed )
                        return testROI
                }
            }
        }   
        Throw( "No irregular ROI found" )
    }
    
    ROI GetFirstOvalROIOfImage( image img )
    {
        if ( img.ImageIsValid() ) 
        {
            if ( 0 != img.ImageCountImageDisplays() ) 
            {
                imageDisplay disp = img.ImageGetImageDisplay( 0 )
                number nRois = disp.ImageDisplayCountROIs()
                for ( number i = 0; i < nRois; i++ )
                {
                    ROI testROI = disp.ImageDisplayGetRoi( i )
                    if ( testROI.ROIIsOval() )
                        return testROI
                }
            }
        }   
        Throw( "No oval ROI found" )
    }
    
    void MyAddOvalROIToMask( image img, ROI ovalROI )
    {
        number top, left, bottom, right
        ovalROI.ROIGetOval( top, left, bottom, right )
        number sx = ( right - left )
        number sy = ( bottom - top )
        number cx = sx/2    // Used as both center x coordiante and x radius!
        number cy = sy/2    // Used as both center y coordiante and y radius!
        
        // Create mask of just the rect area
        image maskCut := RealImage( "", 4, sx, sy )
        maskCut = ( ((cx-icol)/cx)**2 + ((cy-irow)/cy)**2 <= 1 ) ? 1 : 0
        
        // Apply mask to image
        img[top, left, bottom, right] = maskCut
    }
    
    number GetROIMean( image img, ROI theRoi )
    {
        if ( !img.ImageIsValid() ) Throw( "Invalid image in GetROIMean()" )
        if ( !theRoi.ROIIsValid() ) Throw( "Invalid roi in GetROIMean()" )
        
        // Create a binary mask of "img" size using the ROI's coordinates
        image mask = img * 0;   // image of same size as "img" with 0 values
        number sx, sy
        img.GetSize( sx, sy )
        
        // Oval ROIs are not supported by the command correctly
        // Hence check and compute mask manually..
        if ( theROI.ROIIsOval() )
            MyAddOvalROIToMask( mask, theROI )
        else
            theROI.ROIAddToMask( mask, 0, 0, sx, sy )
        
        if ( TwoButtonDialog( "Show mask?", "Yes", "No" ) )
            mask.ShowImage()
        
        // Do meanValue as sums of masked points
        number maskedPoints = sum( mask )
        number maskedSum
        if ( 0 < maskedPoints ) 
            maskedSum = sum( mask * img ) / maskedPoints 
        else
            maskedSum = sum( img )
        
        return maskedSum 
    }
    
    Result( "\n Testing irregular and oval ROIs on image.\n" )
    image testImg := CreateImageWithROI()
    ROI   testROIir  = GetFirstIrregularROIOfImage( testImg )
    number ROIirMean = GetROIMean( testImg, testROIir )
    Result( "\n Mean value (irregular ROI): "+ ROIirMean )
    
    ROI   testROIoval  = GetFirstOvalROIOfImage( testImg )
    number ROIovalMean = GetROIMean( testImg, testROIoval )
    Result( "\n Mean value (oval ROI)     : "+ ROIovalMean )