IPSDK  4_1_0_2
IPSDK : Image Processing Software Development Kit

tutorial for basic 2d quantification process More...

tutorial for basic 2d quantification process

Overview

This script introduce basics for 2d quantification problems resolution using IPSDK library. The following figure presents such problems :

Sample_Quantification2d.png

It presents some classical processing steps used during 2d quantification :

Usage

The application can be called through a command line as follows:

   <application_exe_filename> [--inputImgFilePath <input_image_file_path>] [--outputReportPath <output_report_file_path>] [--inHalfKnlSize <half_kernel_size>] [--inSpaceSigma <space_sigma_value>] [--inDilateFactor <dilate_factor>]
     
   Arguments:
      --inputImgFilePath  optional; specifies the name of the TIFF file, from
                          which the input grey level image will be loaded; if not 
                          specified by the user, the input image is loaded from
                          file <DEV_ROOT>/data/Sample/images/blobs_483x348_UInt8.tif
                          
      --outputReportPath  optional; specifies the name of the CSV file, in
                          which the output quantification will be saved; if not 
                          specified by the user, the output results file is saved to
                          file <TEMPORARY_IPSDK_DIR>/Sample/quantification2d.csv

      --inHalfKnlSize     optional; specifies the value of half kernel size along each axis,
                          used while applying the mean smoothing filter; if not
                          specified by the user, equals to 3 by default

      --inSpaceSigma      optional; specifies the value of the spatial Gaussian standard deviation,
                          used while applying the 2d separated bilateral filter; if not
                          specified by the user, equals to 8 by default

      --inDilateFactor    optional; specifies the value of the dilation factor
                          used while applying the 2d watershed-based binary separation; if not
                          specified by the user, equals to 5 by default

Source code documentation

We start by importing all necessary libraries:

// --- IPSDK includes
// ------------------
// used to initialize IPSDK environment
#include <IPSDKCore/Config/LibraryInitializer.h>
// used to manage exceptions possibly thrown by algoritms functions
#include <IPSDKBaseProcessing/Logger/IPSDKBaseProcessingException.h>
// used to catch exceptions potentially thrown by functions loadTiffImageFile and saveTiffImageFile
#include <IPSDKImageFile/Logger/IPSDKImageFileException.h>
// used to read/write an image from/to a TIFF file:
// used to retrieve usual folders (IPSDK temporary folder, root development folder, etc.)
// used to compute the separated bilateral 2d smoothing
// used to compute the Otsu threshold
// used to compte the 2d binary watershed separation
// used to compute the connected component analysis
// used to compute the 2d label contour extraction
// used to compute measures on shapes
// used for saveCsvMeasureFile
// used for create2dInstance
#include <IPSDKBaseShapeAnalysis/Measure/Info/MeasureInfoSet.h>
// used for createMeasureInfo
// used for the getMeasure() method
#include <IPSDKBaseShapeAnalysis/Measure/MeasureSet.h>
// used to specify the maxFeretDiameter measure parameters
#include <IPSDKIPL/IPSDKIPLShapeAnalysis/Measure/Geometry/FormFactor/MaxFeretDiameter/MaxFeretDiameterMsrParams.h>
// used to retrieve the results of the Area2d measure
#include <IPSDKIPL/IPSDKIPLShapeAnalysis/Measure/Geometry/Basic/Area2d/Area2dMsr.h>
// used to display log messages
// --- third-party boost includes
// ------------------------------
// boost/filesystem/*: contains functions and classes providing facilities to
// manipulate files and directories, and associated paths
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/convenience.hpp>
// boost/program_options/*: contains functions and classes used to manage and
// interpret arguments of command line
#include <boost/program_options/cmdline.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
// --- third-party log4cplus include
// ---------------------------------
// used to add console as output support of logs
#include <log4cplus/consoleappender.h>
// --- STL include
// ---------------
// for std::cout
#include <iostream>

In the main function body, we start by asking to display all the log messages generated by IPSDK libraries and by our application itself to the application console:

int
main(int argc, char* argv[])
{
// add console appender for application logs
log4cplus::SharedAppenderPtr pConsole(new log4cplus::ConsoleAppender);
log4cplus::Logger::getRoot().addAppender(pConsole);
log4cplus::Logger::getRoot().setLogLevel(log4cplus::INFO_LOG_LEVEL);

Next, we initialize the IPSDK environment:

// initialize IPSDK environment (first call to be done before calling any
// function or using any entity of IPSDK environment)
switch(initRes.getResult().value()) {
case ipsdk::core::eLibInitStatus::eLIS_Warn:
// IPSDK library is initialized but there were warnings;
// notify the user by displaying a message
% initRes.getMsg());
break;
case ipsdk::core::eLibInitStatus::eLIS_Failed:
// IPSDK library initialization; notify the user and exit
return -1;
break;
default:
break;
}

Then, we define the input image file path, the output CSV file path and the other parameter values.

// boost objects, used to store input and output files pathes
boost::filesystem::path inputImgPath, outputReportPath;
ipUInt32 inHalfKnlSize;
ipReal32 inDilateFactor;
ipReal64 inSpaceSigma;

We then read the arguments of the command line :

// read program options from command line, and, if appropriate,
// initialize input and output images files paths
if(!readCmdArguments(argc, argv, inputImgPath, outputReportPath, inHalfKnlSize, inSpaceSigma, inDilateFactor)) {
// clear IPSDK environment features; should be called before exiting
// program
return -1;
}

We load the input image from the associated TIFF file, by calling the function ipsdk::image::file::loadTiffImageFile.

// declare the variable that will contain the input image, loaded from TIFF file
try {
// read input image from specified path
pInGreyImg = ipsdk::image::file::loadTiffImageFile(inputImgPath);
} catch(const image::file::IPSDKImageFileException& e) {
// loadTiffImageFile function threw an exception; display error log
// message
% inputImgPath.string() % e.getMsg());
// clear IPSDK environment features; should be called before exiting
// program
// quit the application with an exit code indicating an error
return -1;
}
greyImg.png

The first step consists in input image filtering to remove noise. In this tutorial we use Separated bilateral smoothing 2d filter. The good choice of used filter is a quite difficult task since it is dependent on input image type and on searched shapes.

At this step, the user will have to make a choice which is generally the result of successive try. See Filtering image operations for a list of available filters. There is no universal solution for that step, the user could nevertheless be interested in following questions :

// filtering of input image to smooth defaults
greyFilteredImg.png

Once the input image is filtered, we process to an automatic image binarization. In this tutorial we use Otsu Threshold binarization filter.

// auto threshold on filtered image
ipsdk::image::file::saveTiffImageFile(binImgPath/"binImg.tif", otsuResult._pOutImg);
binImg.png

Please note in previous image connected area enhanced by red circles. During the next step, we will automatically separate these areae using a binary separation technic based on watershed algorithm (Watershed Binary Separation 2d).

// automatic separation of binary shapes
ipsdk::imaproc::attr::eWatershedSeparationMode::eWSM_Split);
ipsdk::image::file::saveTiffImageFile(binImgPath/"binSepImg.tif", pBinSepImg);
binSepImg.png

Once all shape separated, we can proceed to a connected components analysis (Connected Component 2d) :

// connected components analysis of separated binary image
ipsdk::image::file::saveTiffImageFile(binImgPath/"labelImg.tif", pLabelImg);
labelImg.png

And then to the extraction of associated contours (LabelContourExtraction2d_Grp) :

// extraction of contours from connected components image
contours.png

We can now define a collection of measures that should be computed on extracted shapes (see Shape Analysis and Measurement for a complete list of available measures) :

// definition of measure set on shapes
createMeasureInfo(pMeasureInfoSet2d, "Area2dMsr");
//createMeasureInfo(pMeasureInfoSet2d, "EquivalentRayMsr");
//createMeasureInfo(pMeasureInfoSet2d, "AspectRatioMsr"/*, createAspectRatioMsrParams(180)*/);
createMeasureInfo(pMeasureInfoSet2d, "Barycenter2dMsr");
createMeasureInfo(pMeasureInfoSet2d, "HistogramMostPopulatedGLMsr");
//createMeasureInfo(pMeasureInfoSet2d, "MaxFeretDiameterMsr", ipsdk::imaproc::shape::analysis::createMaxFeretDiameterMsrParams(180));
//createMeasureInfo(pMeasureInfoSet2d, "MeanMsr");

And finally compute these measures :

// shape analysis computation
ipsdk::shape::analysis::MeasureSetPtr pMeasureSet = ipsdk::imaproc::shape::analysis::shapeAnalysis2d(pInGreyImg, pShape2dColl, pMeasureInfoSet2d);

At last, we save the measurement report into a csv format.

// save report in csv format
ipsdk::shape::analysis::saveCsvMeasureFile(outputReportPath, *pMeasureSet);
report.png

Data can also be directly accessed by using the following syntax :

// retrieve area measure results
const ipsdk::shape::analysis::MeasureConstPtr& pArea2dOutMsr = pMeasureSet->getMeasure("Area2dMsr");
static_cast<const ipsdk::shape::analysis::ValueMeasureResult<ipReal64>&>(pArea2dOutMsr->getMeasureResult());
// extract associated results collection
const std::vector<ipReal64> areaValues = outAreaMsrResults.getColl();
// access to the first label area
const ipReal64 firstLabelArea = areaValues[1];

See the full source listing