Measurement Browsing
This example shows how to automatically parse the content of a label analysis and export it in a csv file.
First, a grayscale image is opened and segmented to generate a label image containing 8 objects.
Then, an analysis is performed with three measurements selected. It computes for each object its area, a shape factor, and a set of oriented diameters. The FeretDiameter measurement generates by default an array of 10 diameters corresponding to different orientations with a pitch of 18 degrees.
A first loop shows how to introspect the analysis to get the name of each selected measurement and deploy the array of diameters. A second loop shows how to introspect the analysis to print the measurement results for each object and deploys the diameter distribution. This step demonstrates the ability to browse the content of an analysis without making assumptions about the measurements that have been selected within.
In practice, the analysis can be directly converted to a spreadsheet structure with the toDataFrame method. It generates an IOLink DataFrameView object. In Python, this object can be directly printed in the standard output.
Finally, IOFormat allows the export of a DataFrameView object in a csv file that can be visualized as an Excel table. This method is more straightforward for exporting an analysis. The previous one gives you more freedom to customize the export for your needs.
See also
Then, an analysis is performed with three measurements selected. It computes for each object its area, a shape factor, and a set of oriented diameters. The FeretDiameter measurement generates by default an array of 10 diameters corresponding to different orientations with a pitch of 18 degrees.
A first loop shows how to introspect the analysis to get the name of each selected measurement and deploy the array of diameters. A second loop shows how to introspect the analysis to print the measurement results for each object and deploys the diameter distribution. This step demonstrates the ability to browse the content of an analysis without making assumptions about the measurements that have been selected within.
Label | Area2d(mm^2) | InverseCircularity2d() | FeretDiameter2d[0](mm) | FeretDiameter2d[1](mm) |
1 | 1058.00 | 1.23 | 35.00 | 38.73 |
2 | 1361.00 | 3.70 | 47.00 | 49.31 |
3 | 1086.00 | 1.26 | 38.00 | 35.54 |
4 | 293.00 | 1.81 | 20.00 | 18.40 |
In practice, the analysis can be directly converted to a spreadsheet structure with the toDataFrame method. It generates an IOLink DataFrameView object. In Python, this object can be directly printed in the standard output.
Index [label] | Area2d | InverseCircularity2d | ... | FeretDiameter2d[direction=9] |
0 | 1058 | 1.2297213077545166 | ... | 38.72925567626953 |
1 | 1361 | 3.698363780975342 | ... | 49.00188064575195 |
2 | 1086 | 1.2601970434188843 | ... | 42.93851089477539 |
3 | 293 | 1.8089686632156372 | ... | 22.111291885375977 |
Finally, IOFormat allows the export of a DataFrameView object in a csv file that can be visualized as an Excel table. This method is more straightforward for exporting an analysis. The previous one gives you more freedom to customize the export for your needs.
#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 ); // Calibrate this image to match 1 pixel to 1.4 mm Vector3d spacing{ 1.4, 1.4, 1 }; imageLab->setSpatialSpacing( spacing ); imageLab->setSpatialUnit( "mm" ); // Define the analysis features to be computed AnalysisMsr::Ptr analysis = std::make_shared< AnalysisMsr >(); analysis->select( NativeMeasurements::area2d ); analysis->select( NativeMeasurements::inverseCircularity2d ); analysis->select( NativeMeasurements::feretDiameter2d ); // Launch the feature extraction on the segmented image labelAnalysis( imageLab, imageInput, analysis ); // Print the analysis table header std::string lineToPrint( "Label\t" ); for ( const auto& measure : analysis->getMeasurements() ) { // Build and print the table header if ( measure->shape().size() == 1 ) // The measurement is a scalar value lineToPrint += measure->name() + "(" + measure->information().physicalUnit() + ")\t"; else if ( measure->shape().size() == 2 ) // The measurement is an array, loop on it for ( size_t j = 0; j < measure->shape()[1]; ++j ) lineToPrint += measure->name() + "[" + std::to_string( j ) + "](" + measure->information().physicalUnit() + ")\t"; } std::cout << lineToPrint << std::endl; VectorXu64 index; // Print all measurement results for each label for ( int i = 0; i < analysis->labelCount(); ++i ) { lineToPrint = std::to_string( i + 1 ) + "\t"; for ( const auto& measure : analysis->getMeasurements() ) { index = measure->shape(); index[0] = i; if ( measure->shape().size() == 1 ) // The measurement is a scalar value lineToPrint += std::to_string( measure->toDouble( index ) ) + "\t"; else if ( measure->shape().size() == 2 ) // The measurement is an array, loop on it for ( size_t j = 0; j < measure->shape()[1]; ++j ) { index[1] = j; lineToPrint += std::to_string( measure->toDouble( index ) ) + "\t"; } } std::cout << lineToPrint << std::endl; } // Export the analysis in a dataframe and save it in a csv file auto dataframe = analysis->toDataFrame(); writeView( dataframe, "T04_03_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_03_MeasurementBrowsing 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; using System.Linq; namespace T04_03_MeasurementBrowsing { 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 ); // Calibrate this image to match 1 pixel to 1.4 mm Vector3d spacing = new Vector3d( 1.4, 1.4, 1 ); imageLab.SpatialSpacing = spacing; imageLab.SpatialUnit = "mm"; // Define the analysis features to be computed AnalysisMsr analysis = new AnalysisMsr(); analysis.Select( NativeMeasurements.Area2d ); analysis.Select( NativeMeasurements.InverseCircularity2d ); analysis.Select( NativeMeasurements.FeretDiameter2d ); // Launch the feature extraction on the segmented image Processing.LabelAnalysis( imageLab, imageInput, analysis ); // Print the analysis table header string lineToPrint = "Label\t"; foreach ( var measure in analysis.Measurements() ) { // Build and print the table header if ( measure.Shape().Length == 1 ) // The measurement is a scalar value lineToPrint += measure.Name() + "(" + measure.information.physicalUnit + ")\t"; else if ( measure.Shape().Length == 2 ) // The measurement is an array, loop on it for ( int j = 0; j < measure.Shape()[1]; ++j ) lineToPrint += measure.Name() + "[" + j + "](" + measure.information.physicalUnit + ")\t"; } Console.WriteLine( lineToPrint ); int[] index; // Print all measurement results for each label for ( int i = 0; i < analysis.LabelCount(); ++i ) { lineToPrint = ( i + 1 ) + "\t"; foreach ( var measure in analysis.Measurements() ) { index = measure.Shape(); index[0] = i; if ( measure.Shape().Length == 1 ) // The measurement is a scalar value lineToPrint += measure.ToDouble( index.Select( item => ( long )item ).ToArray() ).ToString() + "\t"; else if ( measure.Shape().Length == 2 ) // The measurement is an array, loop on it for ( int j = 0; j < measure.Shape()[1]; ++j ) { index[1] = j; lineToPrint += measure.ToDouble( index.Select( item => ( long )item ).ToArray() ).ToString() + "\t"; } } Console.WriteLine( lineToPrint ); } // Export the analysis in a dataframe and save it in a csv file DataFrameView dataframe = analysis.ToDataFrame(); ViewIO.WriteView( dataframe, "T04_03_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_03_MeasurementBrowsing 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 import iolink try: # Initialize the ImageDev library if not done if not imagedev.is_initialized(): imagedev.init() # 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) # Calibrate this image to match 1 pixel to 1.4 mm image_lab.spatial_spacing = iolink.Vector3d(1.4, 1.4, 1) image_lab.spatial_unit = 'mm' # Define the analysis features to be computed analysis = imagedev.AnalysisMsr() analysis.select(imagedev.native_measurements.Area2d) analysis.select(imagedev.native_measurements.InverseCircularity2d) analysis.select(imagedev.native_measurements.FeretDiameter2d) # Launch the feature extraction on the segmented image imagedev.label_analysis(image_lab, image_input, analysis) # Print the analysis table header line_to_print = 'Label\t' for measure in analysis.measurements: # Build and print the table header if len(measure.shape) == 1: # The measurement is a scalar value line_to_print += measure.name + '(' + measure.information.physical_unit + ')\t' elif len(measure.shape) == 2: # The measurement is an array, loop on it for j in range(0, measure.shape[1]): line_to_print += measure.name + '[' + str(j) + '](' + measure.information.physical_unit + ')\t' print(line_to_print) # Print all measurement results for each label for i in range(0, analysis.label_count): line_to_print = str(i + 1) + '\t\t' for measure in analysis.measurements: if len(measure.shape) == 1: # The measurement is a scalar value line_to_print += '{:.2f}'.format(measure.value(i)) + '\t\t' elif len(measure.shape) == 2: # The measurement is an array, loop on it for j in range(0, measure.shape[1]): line_to_print += '{:.2f}'.format(measure.value(i, j)) + '\t\t\t\t' print(line_to_print) # Export the analysis in a dataframe and save it in a csv file dataframe = analysis.to_data_frame() print(dataframe) ioformat.write_view(dataframe, 'T04_03_analysis.csv') print("This example ran successfully.") except Exception as error: # Print potential exception in the standard output print("T04_03_MeasurementBrowsing exception: " + str(error)) # ImageDev library finalization imagedev.finish()
See also