ImageDev

Custom Analysis

This example shows how to set the attributes of a measurement and how to create a new measurement from a user defined formula.
Several native measurements of ImageDev can be customized by setting some parameters. For instance, the orientation of the Feret diameters to be computed can be user-defined. By default, the Feret diameter measurement contains 10 orientations with a pitch of 18 degrees.

This example shows first how to reduce these orientations to 0 and 90 degrees by selecting two diameters and resampling the distribution between 0 and 180 degrees. The setOrientation method can also be used for this purpose, especially if the expected distribution is not uniform.

Then, this example shows how to create a new measurement from a user-defined formula corresponding to a circularity factor. The validity of the formula is checked, and the new measurement is selected in the analysis.

#include <ImageDev/ImageDev.h>
#include <ioformat/IOFormat.h>
#include <string.h>

using namespace imagedev;
using namespace ioformat;
using namespace iolink;

int
main( int argc, char* argv[] )
{
    int status = 0;

    try
    {
        // ImageDev library initialization if not done
        if ( isInitialized() == false )
            imagedev::init();

        // Open a grayscale image from a tif file
        auto imageInput = readImage( std::string( IMAGEDEVDATA_IMAGES_FOLDER ) + "objects.tif" );

        // Threshold and label the binary input
        auto imageBin = thresholdingByCriterion( imageInput,
                                                 ThresholdingByCriterion::ComparisonCriterion::GREATER_THAN_OR_EQUAL_TO,
                                                 40 );
        auto imageLab = labeling2d( imageBin, Labeling2d::LABEL_8_BIT, Labeling2d::CONNECTIVITY_8 );

        // Change the number of Feret diameter to 2 (0 and 90 degrees)
        MeasurementAttributes::feret2d()->setOrientationCount( 2 );
        MeasurementAttributes::feret2d()->resample();
        std::cout << "FeretDiameter[0] = " + std::to_string( MeasurementAttributes::feret2d()->orientation( 0 ) )
                  << std::endl;
        std::cout << "FeretDiameter[1] = " + std::to_string( MeasurementAttributes::feret2d()->orientation( 1 ) )
                  << std::endl;

        // Create a circularity factor between 0 and 1
        std::string myFormula( "4*Pi*" + NativeMeasurements::area2d->name() + "/" +
                               NativeMeasurements::polygonePerimeter2d->name() + "**2" );
        if ( !checkMeasurementFormula( myFormula ) )
            // Raise an exception if the formula is not valid
            throw imagedev::Exception( "Invalid formula: " + myFormula );

        const auto* myCircularity = AnalysisMsr::registerCustomMeasurement( "MyCircularity",
                                                                            myFormula,
                                                                            "A circularity factor between 0 and 1." );

        // Define the analysis features to be computed
        AnalysisMsr::Ptr analysis = std::make_shared< AnalysisMsr >();
        analysis->select( NativeMeasurements::feretDiameter2d );
        analysis->select( myCircularity );

        // Launch the feature extraction on the segmented image
        labelAnalysis( imageLab, imageInput, analysis );

        // Export the analysis in a dataframe and save it in a csv file
        auto dataframe = analysis->toDataFrame();
        writeView( dataframe, "T04_04_analysis.csv" );

        std::cout << "This example ran successfully." << std::endl;
    }
    catch ( const imagedev::Exception& error )
    {
        // Print potential exception in the standard output
        std::cerr << "T04_05_CustomAnalysis exception: " << error.what() << std::endl;
        status = -1;
    }

    // ImageDev library finalization
    imagedev::finish();

    // Check if we must ask for an enter key to close the program
    if ( !( ( argc == 2 ) && strcmp( argv[1], "--no-stop-at-end" ) == 0 ) )
        std::cout << "Press Enter key to close this window." << std::endl, getchar();

    return status;
}
using System;
using ImageDev;
using IOLink;
using IOFormat;

namespace T04_05_CustomAnalysis
{
    class Program
    {
        static void Main( string[] args )
        {
            int status = 0;

            try
            {
                // Initialize the ImageDev library if not done
                if ( Initialization.IsInitialized() == false )
                    Initialization.Init();

                // Open a grayscale image from a tif file
                ImageView imageInput = ViewIO.ReadImage( "Data/images/objects.tif" );

                // Threshold and label the binary input
                var imageBin = Processing.ThresholdingByCriterion(
                    imageInput, ThresholdingByCriterion.ComparisonCriterion.GREATER_THAN_OR_EQUAL_TO, 40 );
                var imageLab = Processing.Labeling2d( imageBin );

                // Change the number of Feret diameter to 2 (0 and 90 degrees)
                MeasurementAttributes.feret2d().orientationCount = 2;
                MeasurementAttributes.feret2d().Resample();
                Console.WriteLine( "FeretDiameter[0] = " + MeasurementAttributes.feret2d().Orientation( 0 ) );
                Console.WriteLine( "FeretDiameter[1] = " + MeasurementAttributes.feret2d().Orientation( 1 ) );

                // Create a circularity factor between 0 and 1
                string myFormula = "4*Pi*" + NativeMeasurements.Area2d.name + "/" +
                                   NativeMeasurements.PolygonePerimeter2d.name + "**2";
                if ( !Processing.CheckMeasurementFormula( myFormula ) )
                    // Raise an exception if the formula is not valid
                    throw new Exception( "Invalid formula: " + myFormula );

                var myCircularity = AnalysisMsr.RegisterCustomMeasurement( "MyCircularity",
                                                                           myFormula,
                                                                           "A circularity factor between 0 and 1." );

                // Define the analysis features to be computed
                AnalysisMsr analysis = new AnalysisMsr();
                analysis.Select( NativeMeasurements.FeretDiameter2d );
                analysis.Select( myCircularity );

                // Launch the feature extraction on the segmented image
                Processing.LabelAnalysis( imageLab, imageInput, analysis );

                // Export the analysis in a dataframe and save it in a csv file
                DataFrameView dataframe = analysis.ToDataFrame();
                ViewIO.WriteView( dataframe, "T04_05_analysis.csv" );

                // Notify the garbage collector that the created images can be freed
                imageInput.Dispose();
                imageBin.Dispose();
                imageLab.Dispose();

                Console.WriteLine( "This example ran successfully." );
            }
            catch ( Exception error )
            {
                // Print potential exception in the standard output
                System.Console.WriteLine( "T04_05_CustomAnalysis exception: " + error.ToString() );
                status = -1;
            }

            // ImageDev library finalization
            Initialization.Finish();

            // Check if we must ask for an enter key to close the program
            if ( !( ( args.Length >= 1 ) && ( args[0] == "--no-stop-at-end" ) ) )
            {
                System.Console.WriteLine( "Press Enter key to close this window." );
                System.Console.ReadKey();
            }

            System.Environment.Exit( status );
        }
    }
}
import imagedev
import imagedev_data
import ioformat

try:
    # Open a grayscale image from a tif file
    image_input = ioformat.read_image(imagedev_data.get_image_path('objects.tif'))

    # Threshold and label the binary input
    image_bin = imagedev.thresholding_by_criterion(image_input, comparison_value=40)
    image_lab = imagedev.labeling_2d(image_bin, imagedev.Labeling2d.LabelType.LABEL_8_BIT)

    # Change the number of Feret diameter to 2 (0 and 90 degrees)
    imagedev.MeasurementAttributes.feret2d.orientation_count = 2
    imagedev.MeasurementAttributes.feret2d.resample()
    print(f'FeretDiameter[0] = {imagedev.MeasurementAttributes.feret2d.orientation(0)}')
    print(f'FeretDiameter[1] = {imagedev.MeasurementAttributes.feret2d.orientation(1)}')

    # Create a circularity factor between 0 and 1
    my_formula = '4*Pi*' + imagedev.native_measurements.Area2d.name + '/' + \
                 imagedev.native_measurements.PolygonePerimeter2d.name + '**2'

    if not imagedev.check_measurement_formula(my_formula):
        # Raise an exception if the formula is not valid
        raise Exception('Invalid formula ' + my_formula)

    my_circularity = imagedev.AnalysisMsr.register_custom_measurement('MyCircularity', my_formula,
                                                                      'A circularity factor between 0 and 1.')
    # Define the analysis features to be computed
    analysis = imagedev.AnalysisMsr()
    analysis.select(imagedev.native_measurements.FeretDiameter2d)
    analysis.select(my_circularity)

    # Launch the feature extraction on the segmented image
    imagedev.label_analysis(image_lab, image_input, analysis)

    # Print the analysis in the standard output
    print(analysis.to_data_frame())

    print("This example ran successfully.")
except Exception as error:
    # Print potential exception in the standard output
    print("T04_05_CustomAnalysis exception: " + str(error))


See also