% % Complete documentation on the extended LaTeX markup used for Insight % documentation is available in ``Documenting Insight'', which is part % of the standard documentation for Insight. It may be found online % at: % % http://www.itk.org/ \documentclass{InsightArticle} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % hyperref should be the last package to be loaded. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \usepackage[dvips, bookmarks, bookmarksopen, backref, colorlinks,linkcolor={blue},citecolor={blue},urlcolor={blue}, ]{hyperref} % to be able to use options in graphics \usepackage{graphicx} % for pseudo code \usepackage{listings} % subfigures \usepackage{subfigure} % This is a template for Papers to the Insight Journal. % It is comparable to a technical report format. % The title should be descriptive enough for people to be able to find % the relevant document. \title{Labeled object representation and manipulation with ITK} % Increment the release number whenever significant changes are made. % The author and/or editor can define 'significant' however they like. %\release{0.00} % At minimum, give your name and an email address. You can include a % snail-mail address if you like. \author{Ga\"etan Lehmann{$^2$}} \authoraddress{{$^1$}INRA, UMR 1198; ENVA; CNRS, FRE 2857, Biologie du D\'eveloppement et Reproduction, Jouy en Josas, F-78350, France.} \begin{document} \maketitle \ifhtml \chapter*{Front Matter\label{front}} \fi \begin{abstract} \noindent Richard Beare has recently introduced a new filter to efficiently labelize the connected component with ITK, and has also proposed to use the run-length encoding used in that filter to implement some of the most useful binary mathematical morphology operators: the opening by attribute. Following that idea, and after have searched a way to use the ITK's spatial objects for this task, a new set of classes have been developed to represent and manipulate the labeled images and the objects within them in ITK. Those new classes have been used to implement several labeled images manipulation based on object attributes, as well as the binary specialization of some mathematical morphology filter already included in ITK, and not related to the attribute of the objects. With those last filters, this contribution comes with 49 new classes, and should greatly enhance the binary mathematical morphology in ITK. All the source codes are provided, as well as a full set of tests and several usage examples of the new classes. \end{abstract} \tableofcontents \section{Introduction} Identifying the objects in an image is a very common task, often realized by producing an image of the same size with a single pixel value per object. This image is called a labeled image. There are several way to create this image. It can be done by searching the connected components in a binary image, it can be produced directly by some algorithms, like the watershed transform, it can even be simply done by hand, etc. \section{Definitions} In that article, some terms will be cited very frequently. I will try to define them, in the context of the image analysis. \subsection{Label} A label is an identifier of something with the same caracteristics in the image. Those caracteristics can be whatever you want, for example, the range of pixel values, the same object in sense of connected component, etc. A label can be represented by anything and only need to be unique in the image. It doesn't even require to be ordered. In practice, we choose to use the integral number types, for several reasons: they are commonly used in image analysis, they efficiently reprensent the label in memory, and its easy to find the next label by adding 1. \subsection{Labeled image} A label image is an image which contains several labeled pixels. Often, the labels are representing some objects placed on a background, and so the label image may use a particular label for the background. \subsection{Binary image} A binary image is an image with two labels: a foreground label and a background label. In practice, the binary images are using a pixel type able to store more than those two values. The foreground is thus defined with a particular label, and the other label in the image are considered as the background. A side effect of that is that a labeled image can be considered as a binary image, and so, it let us manipulate a single object in a labeled image. \subsection{Attribute} An attribute is a value of any type associated with a label. It can be for example the size of an object, the mean of its pixels intensities, etc. \section{Existing classes and naming convention in ITK} In ITK, the labeled and the binary images are implemented as a simple {\em itk::Image}. The pixel types used are most of the time integral, signed or unsigned, but may be of other types. Several definitions of a binary image or used in ITK. Depending of the class which implement it, a binary image can be: \begin{itemize} \item All the pixels with a given value are in the foreground. The others are in the background. That's the definition proposed in that article. \item All the pixels with a given value are in the background. The others are in the foreground. \item All the pixels greater than a value (zero by default, or the mean of the maximum value in the image and the minimum value in the image) are in the foreground. The other are in the background. This definition is often used in the levelset framework, where a border can be defined at a subpixel resolution. \end{itemize} All those definitions should be uniformized to enhance user experience with ITK. In that article (and all the others from the same author), the first one is the only one used. The filter which are mainpulating binary images are often prefixed with the word "Binary", to differenciate the grayscale version which don't have a prefix. It seem to be a quite good practice which have been kept in that article. The filter dedicated to the manipulation of labeled images have the word "Label" somewhere in there name. Again, it seem to be a good practice which have been kept in that article. \section{Data representation} The labeled images are often used the represent the connected components of an image. In this contribution, another representation has been chosen. The objects contained in the image, as connected component, can be efficiently stored in memory as a set of lines, using the run-length encoding: a starting point for each line, and the length of the line on a given dimension (by convention, the dimension 0). The image is a collection of those objects, which also store some values of the image, like its size, its spacing, etc. \subsection{itk::LabelCollectionImage} The {\em itk::LabelCollectionImage} class is in charge of managing the collection of labeled objects of the image, as well as storing the metadata associated with the image like the spacing, the physical position - all the metadata found in {\em itk::Image}. The {\em itk::LabelCollectionImage} provide a part of the API of the {\em itk::Image} class, and so can be manipulated as an image\footnote{It doesn't support the itk::Image iterators though} in many cases. The performance can be very different however, because of the very different data structure used. The {\em itk::LabelCollectionImage} is a templated class, which take a single parameter: the type of {\em labeled object} stored by that class. The dimension of the image is took from the {\em labeled object} class, and thus don't need to be defined as template parameter of that class. The pixel type of the image also comes from the {\em labeled object} class. \subsection{itk::LabelObject and its specializations} The {\em itk::LabelObject} class represent the label obejcts. It has two main features: \begin{itemize} \item It manage the set pixels which compose the object. The pixels are stored using the run-length encoding. \item It has a label. \end{itemize} No attribute are stored in this class, which can thus be seen as the base class for the objects with attributes, or which can be used when no attributes are required. The {\em itk::LabelObject} class is templated and takes to required template parameters: \begin{itemize} \item the type of the label, \item the dimension of the image. \end{itemize} Several subclasses are provided with that contribution, to cover the most common usages of the labeled objects manipulation: \begin{itemize} \item {\em itk::AttributeLabelObject} is able to store a generic attribute. It is generic in the sense that its type is given in template parameter. \item {\em itk::ShapeLabelObject} contains numerous attribute related to the shape of the labeled object. Computing the values of those attributes does not require a feaure image. \item {\em itk::StatisticsLabelObject} contains numerous statistics about the grey values of a feature image in the same place than the labeled object. Computing the values of those attributes {\em does} require a feature image. \end{itemize} The classes {\em itk::ShapeLabelObject} and {\em itk::StatisticsLabelObject} have been created to reduce the number of filters made to manipulate the attributes, and to make the computation of all the set of attributes much efficient. In the early stage of development, all the attributes were managed as in {\em AttributeLabelObject}, and a set of 8 classes made to manipulate a single attribute were provided, leading to a huge number of classes. The scalar values of the attributes of the {\em itk::ShapeLabelObject} and the {\em itk::StatisticsLabelObject} classes are often given both in pixel and in physical units, in order to be able to give some parameter independant of the image spacing. Both {\em itk::ShapeLabelObject} and {\em itk::StatisticsLabelObject} are templated classes. They take the same template parameters than the {\em itk::LabelObject} class. The 2 first template parameters of the {\em itk::AttributeLabelObject} class or the same than the {\em itk::LabelObject} class. The third one is the attribute type. \subsubsection{itk::ShapeLabelObject attributes} \begin{itemize} \item {\em Size} is the size of the object in number of pixels. Its type is {\em unsigned long}. \item {\em PhysicalSize} is the size of the object in physical unit. It is equal to the {\em Size} multiplicated by the physical pixel size. Its type is {\em double}. \item {\em Centroid} is the position of the center of the shape in physical coordinates. It is not constrained to be in the object, and thus can be outside if the object is not convex. Its type is {\em Point< double, ImageDimension >}. \item {\em Region} is the bounding box of the object given in the pixel coordinates. The physical coordinate can easily be computed from it. Its type is {\em ImageRegion< ImageDimension >}. \item {\em RegionElongation} is the ratio of the longest physical size of the region on one dimension and its smallest physical size. This descriptor is not robust, and in particular is sensitive to rotation. Its type is {\em double}. \item {\em SizeRegionRatio} is the ratio of the size of the object region (the bounding box) and the real size of the object. Its type is {\em double}. \item {\em SizeOnBorder} is the number of pixels in the objects which are on the border of the image. This attribute is particulary useful to remove the objects which are touching too much the border. Its type is {\em unsigned long}. \item {\em FeretDiameter} is the diameter in physical units of the sphere which include all the object. The feret diameter is computed by default only if the dimension of the image is $2$, because of its high computation cost for higher dimensions. Its type is {\em double}. % \item {\em Perimeter} \end{itemize} \subsubsection{itk::StatisticsLabelObject attributes} \begin{itemize} \item {\em Minimum} is the minimum value in the feature image for the object. Its type is the feature image pixel type. \item {\em MinimumIndex} is the index position in the image where the first minimum was found. Its type is{\em Index< ImageDimension >}. \item {\em Maximum} is the maximum value in the feature image for the object. Its type is the feature image pixel type. \item {\em MaximumIndex} is the index position in the image where the first maximum was found. Its type is{\em Index< ImageDimension >}. \item {\em Mean} is the mean of the pixel values in the object. Its type is {\em double}. \item {\em Sum} is the sum of all the pixel values in the objects. Its type is {\em double}. \item {\em Sigma} is the standard deviation of the pixels values in the objects. Its type is {\em double}. \item {\em Variance} is the variance of the pixels values in the objects. Its type is {\em double}. \item {\em Median} is the median of the pixels values in the obejct. Its type is {\em double} \item {\em CenterOfGravity} is the center of gravity of the object. It type is {\em Point< double >}. % \item {\em CentralMoments} % \item {\em PrincipalMoments} % \item {\em PrincipalAxes} \item {\em Kurtosis} is the kurtosis of the pixel values in the objects. Its type is {\em double}. \item {\em Skewness} is the skewness of the pixel values in the objects. Its type is {\em double}. \end{itemize} \subsection{itk::LabelObjectLine} {\em itk::LabelObjectLine} is the object used to store the position and the size of a single line. \section{General view of the usage} \subsection{Generating the itk::LabelCollectionImage} The {\em itk::LabelCollectionImage} class provide some methods to fill the image "by hand", like the usual {\em SetPixel()} method. However, the most efficient way is to convert a labeled image or a binary image stored in an {\em itk::Image} to a {\em itk::LabelCollectionImage}, by using {\em itk::BinaryImageToLabelCollectionImageFilter} or {\em itk::LabelImageToLabelCollectionImageFilter}. \subsection{Valuating the attributes} The label objects produced by those filters have no attribute value set, and thus, the attributes must be valuated. Some filters are provided for the most common used ones: \begin{itemize} \item {\em itk::ShapeLabelCollectionImageFilter} to fill the attributes of the {\em itk::ShapeLabelObject}s, \item and {\em itk::ShapeLabelCollectionImageFilter} to fill the attributes of the {\em itk::StatisticsLabelObject}s. \end{itemize} For the {\em itk::AttributeLabelObject} class or other classes, the user must set the value by himself, for example by implementing a subclass of {\em itk::InPlaceLabelCollectionImageFilter}. \subsection{Manipulating the itk::LabelCollectionImage} Once created and, optionally, valuated, several filters are provided to manipulate the {\em itk::LabelCollectionImage}: \begin{itemize} \item An opening can be performed with the {\em OpeningLabelCollectionImageFilter} classes. Those classes will remove all the objects with an attribute value lower or greater than a given value. Because we often can use some criteria which have not been used during the segmentation procedure, like the size of the object, the mean value of its pixels, etc., the attribute opening is often a very efficient way to enhance a segmentation. For example, after a thresholding of a grayscale image, the objects too small or too beg to be of interest can be removed that way. \item A fixed number of objects can be kept, with the {\em KeepNObjectsLabelCollectionImageFilter} classes. They are chosen according to the value of their attribute. The user can choose to keep the ones with the highest, or with the lowest attribute values. \item The objects can be relabel, with the {\em RelabelLabelCollectionImageFilter} classes. The order of the label is dependant of the value of the attribute. Again, the user can choose to have the objects with the highest attribute value in the first labels, or to have the objects with the lowest attribute values in the first labels. \end{itemize} It can also be useful to simply get the attribute values associated with the objects. In that case, the classes provided in with that article can be used in place of {\em itk::LabelStatisticsImageFilter}, or to get some data about the shape or the position of the object. Finally, it has been chosen to develop a specific filter for the morphological reconstruction. It would have been possible to implement the reconstruction with the most common case (build the object collection, valuate the attributes filter the object, and rebuild the image), but in order to make it compatible with the {\em ShapeLabelObject} and {\em StatisticsLabelObject}, the reconstruction filter filters the collection directly, without setting an attribute in the objects \footnote{I'm not really pleased with that design though, and I'thinking about reimplementing it using the generic attributes. It would have no impact on the binary filters API.}. \subsection{Generating an itk::Image from the itk::LabelCollectionImage} Once the manipulation of the objects is done, it can be useful to go back to a more classic {\em itk::Image}. Several classes are provided to do that: \begin{itemize} \item The {\em itk::LabelCollectionImageToLabelImageFilter} class simply convert a {\em itk::LabelCollectionImage} to a labeled image stored in a {\em itk::Image}. \item The {\em itk::LabelCollectionImageToBinaryImageFilter} put all the objects in the foreground of a binary image stored in a {itk::Image}. It is intended to be used with an image produced by the {\em itk::BinaryImageToLabelCollectionImageFilter}. The background values of the original image can also be restored by this filter. \item The {\em itk::LabelCollectionImageToMaskImageFilter} class can be used to mask an image with the objects of the {\em itk::LabelCollectionImage}. \item Finally, {\em itk::LabelCollectionImageToAttributeImageFilter} produce an {\em itk::Image} with the value of the attribute of the objects of the {\em itk::LabelCollectionImage}. This filter is mostly useful to have a global view of the attribute values in the image. \end{itemize} \section{Prebuilt mini-pipeline filters} The general view of the previous section show a very common way to use those classes. To make easier to use, some prebuilt classes have been made, to perform the mini-pipeline: \begin{itemize} \item creation of the {\em itk::LabelCollectionImage} from an {\em itk::Image}, \item valuation of the attribute(s) of the objects, \item filtering of the {\em itk::LabelCollectionImage}, \item creation of an {\em itk::Image} from the filtered {\em itk::LabelCollectionImage}, \end{itemize} with a specific attribute. Because the objects are often get from a labeled image or from a binary image, those filters have been made for binary, and labeled images. \subsection{Binary filters} \begin{itemize} \item {\em itk::BinaryAttributeKeepNObjectsImageFilter} \item {\em itk::BinaryAttributeOpeningImageFilter} \item {\em itk::BinaryShapeKeepNObjectsImageFilter} \item {\em itk::BinaryShapeOpeningImageFilter} \item {\em itk::BinaryStatisticsKeepNObjectsImageFilter} \item {\em itk::BinaryStatisticsOpeningImageFilter} \end{itemize} \subsection{Label filters} \begin{itemize} \item {\em itk::LabelAttributeKeepNObjectsImageFilter} \item {\em itk::LabelAttributeOpeningImageFilter} \item {\em itk::LabelShapeKeepNObjectsImageFilter} \item {\em itk::LabelShapeOpeningImageFilter} \item {\em itk::LabelStatisticsKeepNObjectsImageFilter} \item {\em itk::LabelStatisticsOpeningImageFilter} \item {\em itk::ShapeRelabelImageFilter} \item {\em itk::StatisticsRelabelImageFilter} \end{itemize} \section{Binary specialization of mathematical morphology filters} \begin{itemize} \item {\em itk::BinaryClosingByReconstructionImageFilter} \item {\em itk::BinaryFillholeImageFilter} \item {\em itk::BinaryGrindPeakImageFilter} \item {\em itk::BinaryOpeningByReconstructionImageFilter} \item {\em itk::BinaryReconstructionByDilationImageFilter} \item {\em itk::BinaryReconstructionByErosionImageFilter} \end{itemize} \section{Usage examples} \subsection{Prebuilt pipelines} \subsubsection{Binary shape opening} The source code is available in the file {\em binary\_shape\_opening.cxx}. \small \begin{verbatim} #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkSimpleFilterWatcher.h" #include "itkBinaryShapeOpeningImageFilter.h" int main(int argc, char * argv[]) { if( argc != 9 ) { std::cerr << "usage: " << argv[0] << " input output foreground background lambda reverseOrdering connectivity attribute" << std::endl; // std::cerr << " : " << std::endl; exit(1); } const int dim = 3; typedef itk::Image< unsigned char, dim > IType; typedef itk::ImageFileReader< IType > ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[1] ); typedef itk::BinaryShapeOpeningImageFilter< IType > BinaryOpeningType; BinaryOpeningType::Pointer opening = BinaryOpeningType::New(); opening->SetInput( reader->GetOutput() ); opening->SetForegroundValue( atoi(argv[3]) ); opening->SetBackgroundValue( atoi(argv[4]) ); opening->SetLambda( atof(argv[5]) ); opening->SetReverseOrdering( atoi(argv[6]) ); opening->SetFullyConnected( atoi(argv[7]) ); opening->SetAttribute( argv[8] ); itk::SimpleFilterWatcher watcher(opening, "filter"); typedef itk::ImageFileWriter< IType > WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetInput( opening->GetOutput() ); writer->SetFileName( argv[2] ); writer->Update(); return 0; } \end{verbatim} \normalsize \subsubsection{Statistics relabel} The source code is available in the file {\em statistics\_relabel.cxx}. \small \begin{verbatim} #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkSimpleFilterWatcher.h" #include "itkStatisticsRelabelImageFilter.h" int main(int argc, char * argv[]) { if( argc != 8 ) { std::cerr << "usage: " << argv[0] << " input input output background useBg reverseOrdering attribute" << std::endl; // std::cerr << " : " << std::endl; exit(1); } const int dim = 3; typedef itk::Image< unsigned char, dim > IType; typedef itk::ImageFileReader< IType > ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[1] ); ReaderType::Pointer reader2 = ReaderType::New(); reader2->SetFileName( argv[2] ); typedef itk::StatisticsRelabelImageFilter< IType, IType > RelabelType; RelabelType::Pointer relabel = RelabelType::New(); relabel->SetInput( reader->GetOutput() ); relabel->SetFeatureImage( reader2->GetOutput() ); relabel->SetBackgroundValue( atoi(argv[4]) ); relabel->SetUseBackground( atoi(argv[5]) ); relabel->SetReverseOrdering( atoi(argv[6]) ); relabel->SetAttribute( argv[7] ); itk::SimpleFilterWatcher watcher(relabel, "filter"); typedef itk::ImageFileWriter< IType > WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetInput( relabel->GetOutput() ); writer->SetFileName( argv[3] ); writer->Update(); return 0; } \end{verbatim} \normalsize \subsubsection{Label shape keep N obejcts} The source code is available in the file {\em label\_shape\_keep\_n\_objects.cxx}. \small \begin{verbatim} #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkSimpleFilterWatcher.h" #include "itkLabelShapeKeepNObjectsImageFilter.h" int main(int argc, char * argv[]) { if( argc != 7 ) { std::cerr << "usage: " << argv[0] << " input output background nb reverseOrdering attribute" << std::endl; // std::cerr << " : " << std::endl; exit(1); } const int dim = 3; typedef itk::Image< unsigned char, dim > IType; typedef itk::ImageFileReader< IType > ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[1] ); typedef itk::LabelShapeKeepNObjectsImageFilter< IType > LabelOpeningType; LabelOpeningType::Pointer opening = LabelOpeningType::New(); opening->SetInput( reader->GetOutput() ); opening->SetBackgroundValue( atoi(argv[3]) ); opening->SetNumberOfObjects( atoi(argv[4]) ); opening->SetReverseOrdering( atoi(argv[5]) ); opening->SetAttribute( argv[6] ); itk::SimpleFilterWatcher watcher(opening, "filter"); typedef itk::ImageFileWriter< IType > WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetInput( opening->GetOutput() ); writer->SetFileName( argv[2] ); writer->Update(); return 0; } \end{verbatim} \normalsize \subsubsection{Binary fill holes} The source code is available in the file {\em binary\_fillhole.cxx}. \small \begin{verbatim} #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkCommand.h" #include "itkSimpleFilterWatcher.h" #include "itkLabelObject.h" #include "itkLabelCollectionImage.h" #include "itkBinaryFillholeImageFilter.h" int main(int argc, char * argv[]) { if( argc != 5 ) { std::cerr << "usage: " << argv[0] << " input output conn fg" << std::endl; // std::cerr << " : " << std::endl; exit(1); } const int dim = 2; typedef itk::Image< unsigned char, dim > IType; typedef itk::ImageFileReader< IType > ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[1] ); reader->Update(); typedef itk::BinaryFillholeImageFilter< IType > I2LType; I2LType::Pointer reconstruction = I2LType::New(); reconstruction->SetInput( reader->GetOutput() ); reconstruction->SetFullyConnected( atoi(argv[3]) ); reconstruction->SetForegroundValue( atoi(argv[4]) ); // reconstruction->SetBackgroundValue( atoi(argv[5]) ); itk::SimpleFilterWatcher watcher(reconstruction, "filter"); typedef itk::ImageFileWriter< IType > WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetInput( reconstruction->GetOutput() ); writer->SetFileName( argv[2] ); writer->Update(); return 0; } \end{verbatim} \normalsize \subsection{LabelObject and LabelCollectionImage manipulation} % \subsubsection{LabelObject} % % TODO % % \subsubsection{ShapeLabelObject} % % TODO % % \subsubsection{StatisticsLabelObject} % % TODO % \subsubsection{AttributeLabelObject} The {\em AttributeLabelObject} let the user specify the type of the attribute he wants to use, and thus is the good choice to implement a new attribute. The source code is available in the file {\em generic\_attribute.cxx}. First we include the headers of the class we will use, and parse the command line. \small \begin{verbatim} #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkAttributeLabelObject.h" #include "itkLabelCollectionImage.h" #include "itkLabelImageToLabelCollectionImageFilter.h" #include "itkAttributeKeepNObjectsLabelCollectionImageFilter.h" #include "itkAttributeOpeningLabelCollectionImageFilter.h" #include "itkAttributeRelabelLabelCollectionImageFilter.h" #include "itkLabelCollectionImageToAttributeImageFilter.h" #include "itkLabelCollectionImageToLabelImageFilter.h" int main(int argc, char * argv[]) { if( argc != 10 ) { std::cerr << "usage: " << argv[0] << " label input attr keep open relabel bg lambda nb" << std::endl; // std::cerr << " : " << std::endl; exit(1); } \end{verbatim} \normalsize Declare the dimension used, and the type of the image for input and output. \small \begin{verbatim} const int dim = 2; typedef unsigned char PType; typedef itk::Image< PType, dim > IType; \end{verbatim} \normalsize The AttributeLabelObject class take 3 template parameters: the 2 ones from the LabelObject class, and the type of the attribute associated with each node. Here we have chosen a double. We then declares the type of the LabelCollectionImage with the type of the label object. \small \begin{verbatim} typedef itk::AttributeLabelObject< unsigned long, dim, double > LOType; typedef itk::LabelCollectionImage< LOType > LCIType; \end{verbatim} \normalsize We read the input images. \small \begin{verbatim} typedef itk::ImageFileReader< IType > ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[1] ); ReaderType::Pointer reader2 = ReaderType::New(); reader2->SetFileName( argv[2] ); \end{verbatim} \normalsize And convert the label image to a LabelCollectionImage. \small \begin{verbatim} typedef itk::LabelImageToLabelCollectionImageFilter< IType, LCIType > I2LType; I2LType::Pointer i2l = I2LType::New(); i2l->SetInput( reader->GetOutput() ); i2l->SetBackgroundValue( atoi(argv[7]) ); \end{verbatim} \normalsize The next step is made outside the pipeline model, so we call Update() now. \small \begin{verbatim} i2l->Update(); reader2->Update(); \end{verbatim} \normalsize Now we will valuate the attribute. The attribute will be the mean of the pixels values in the 2nd image. Note that the StatisticsLabelObject can give us that value, without having to code that by hand - that's an example. Lets begin by declaring the iterator for the objects in the image, and get the object container, to reuse it later. \small \begin{verbatim} LCIType::LabelObjectContainerType::const_iterator it; LCIType::Pointer labelCollection = i2l->GetOutput(); const LCIType::LabelObjectContainerType & labelObjectContainer = labelCollection->GetLabelObjectContainer(); \end{verbatim} \normalsize Now iterate over all the objects in the image. \small \begin{verbatim} for( it = labelObjectContainer.begin(); it != labelObjectContainer.end(); it++ ) { \end{verbatim} \normalsize The label is there if we need it, but it can also be found at labelObject->GetLabel(). \small \begin{verbatim} const PType & label = it->first; LOType * labelObject = it->second; \end{verbatim} \normalsize Init the variables used for the computation. \small \begin{verbatim} double mean = 0; unsigned long size = 0; \end{verbatim} \normalsize Create the iterator for the lines, and iterate over them \small \begin{verbatim} LOType::LineContainerType::const_iterator lit; LOType::LineContainerType lineContainer = labelObject->GetLineContainer(); for( lit = lineContainer.begin(); lit != lineContainer.end(); lit++ ) { const LCIType::IndexType & firstIdx = lit->GetIndex(); const unsigned long & length = lit->GetLength(); size += length; \end{verbatim} \normalsize Then iterate over all the pixels in the line, and get the pixel values in the feature image to compute their mean. \small \begin{verbatim} long endIdx0 = firstIdx[0] + length; for( LCIType::IndexType idx = firstIdx; idx[0]GetOutput()->GetPixel( idx ); } } \end{verbatim} \normalsize Complete the compuation of the mean, and set it as attibute value for the current object. \small \begin{verbatim} mean /= size; labelObject->SetAttribute( mean ); \end{verbatim} \normalsize The LabelObject class provides a Print() method to display its ivars. \small \begin{verbatim} labelObject->Print( std::cout ); } \end{verbatim} \normalsize Now that the objects have their attribute, we are free to manipulate them with the common filters, or by hand. The default accessor (AttributeLabelObject) is the wright one when using AttributeLabelObject so we don't have to specify it. A different one can be used if needed though. \small \begin{verbatim} typedef itk::AttributeKeepNObjectsLabelCollectionImageFilter< LCIType > KeepType; KeepType::Pointer keep = KeepType::New(); keep->SetInput( labelCollection ); keep->SetReverseOrdering( true ); keep->SetNumberOfObjects( atoi(argv[9]) ); \end{verbatim} \normalsize Prevent the filter to run in place, so the input image is not modified. \small \begin{verbatim} keep->SetInPlace( false ); typedef itk::AttributeOpeningLabelCollectionImageFilter< LCIType > OpeningType; OpeningType::Pointer opening = OpeningType::New(); opening->SetInput( labelCollection ); opening->SetLambda( atof(argv[8]) ); keep->SetInPlace( false ); typedef itk::AttributeRelabelLabelCollectionImageFilter< LCIType > RelabelType; RelabelType::Pointer relabel = RelabelType::New(); relabel->SetInput( labelCollection ); keep->SetInPlace( false ); \end{verbatim} \normalsize The attribute values can be put directly in a classic image. \small \begin{verbatim} typedef itk::LabelCollectionImageToAttributeImageFilter< LCIType, IType > A2IType; A2IType::Pointer a2i = A2IType::New(); a2i->SetInput( labelCollection ); \end{verbatim} \normalsize Or the label collection can be converted back to an label image, or to a binary image (not shown here) \small \begin{verbatim} typedef itk::LabelCollectionImageToLabelImageFilter< LCIType, IType > L2IType; L2IType::Pointer l2i = L2IType::New(); \end{verbatim} \normalsize Finally, write the results \small \begin{verbatim} typedef itk::ImageFileWriter< IType > WriterType; WriterType::Pointer writer = WriterType::New(); writer->SetInput( a2i->GetOutput() ); writer->SetFileName( argv[3] ); writer->Update(); writer->SetInput( l2i->GetOutput() ); l2i->SetInput( keep->GetOutput() ); writer->SetFileName( argv[4] ); writer->Update(); l2i->SetInput( opening->GetOutput() ); writer->SetFileName( argv[5] ); writer->Update(); l2i->SetInput( relabel->GetOutput() ); writer->SetFileName( argv[6] ); writer->Update(); return 0; } \end{verbatim} \normalsize \subsection{Reading attribute values} In that example, we will read a binary image, and get some of attributes about the obejcts contained in that image. The source code is available in the file {\em attribute\_values.cxx}. First include the classes we'll use \small \begin{verbatim} #include "itkImageFileReader.h" #include "itkShapeLabelObject.h" #include "itkLabelCollectionImage.h" #include "itkBinaryImageToLabelCollectionImageFilter.h" #include "itkShapeLabelCollectionImageFilter.h" int main(int, char * argv[]) { const int dim = 2; \end{verbatim} \normalsize then declare the type of the input image \small \begin{verbatim} typedef unsigned char PixelType; typedef itk::Image< PixelType, dim > ImageType; \end{verbatim} \normalsize read the input image \small \begin{verbatim} typedef itk::ImageFileReader< ImageType > ReaderType; ReaderType::Pointer reader = ReaderType::New(); reader->SetFileName( argv[1] ); \end{verbatim} \normalsize define the object type. Here the ShapeLabelObject type is chosen in order to read some attribute related to the shape of the objects (by opposition to the content of the object, with the StatisticsLabelObejct). \small \begin{verbatim} typedef unsigned long LabelType; typedef itk::ShapeLabelObject< LabelType, dim > LabelObjectType; typedef itk::LabelCollectionImage< LabelObjectType > LabelCollectionType; \end{verbatim} \normalsize convert the image in a collection of objects \small \begin{verbatim} typedef itk::BinaryImageToLabelCollectionImageFilter< ImageType, LabelCollectionType > ConverterType; ConverterType::Pointer converter = ConverterType::New(); converter->SetInput( reader->GetOutput() ); converter->SetForegroundValue( 200 ); \end{verbatim} \normalsize and valuate the attributes with the dedicated filter: ShapeLabelCollectionImageFilter \small \begin{verbatim} typedef itk::ShapeLabelCollectionImageFilter< LabelCollectionType > ShapeFilterType; ShapeFilterType::Pointer shape = ShapeFilterType::New(); shape->SetInput( converter->GetOutput() ); \end{verbatim} \normalsize update the shape filter, so its output will be up to date \small \begin{verbatim} shape->Update(); \end{verbatim} \normalsize then we can read the attribute values we're interested in. {\em BinaryImageToLabelCollectionImageFilter} produces consecutives labels, so a simple {for} loop will do the job. \small \begin{verbatim} LabelCollectionType::Pointer collection = shape->GetOutput(); for( int label=1; labelGetNumberOfObjects(); label++ ) { LabelObjectType::Pointer labelObject = collection->GetLabelObject( label ); std::cout << label << "\t" << labelObject->GetPhysicalSize() << "\t" << labelObject->GetCentroid() << std::endl; } return 0; } \end{verbatim} \normalsize \section{Threading support} When possible, the filters provided with that contribution have been multithreaded. Some of them however, are not (easily) threadable (the {\em KeepNObjects} and {\em Relabel} filters), are shouldn't get any performance improvement in a threaded version (the {\em Opening} filters). The {\em BinaryImageToLabelCollectionImageFilter} class is a slight modification of the Richard Beare's {\em ConnectedComponentImageFilter}, and thus, has not been threaded. It should however be possible to increase its performance that way. The classical thread architecture is used when the input image is an {\em Image}: the image is splitted in several regions (one per thread), and each thread work on its own region. Because the {\em LabelCollectionImage} image is not an array of pixels, it can't be splitted that way. Instead, several threads are created, and try to take an object in the collection. If they get one, they process that object individually, and try to get another one when the object is processed. If no object can be get, the thread ends. A {\em FastMutexLock} is used to ensure that only one thread take an object at a time. For the developer, the usage of the threading support is made very simple, by subclassing {\em LabelCollectionImageFilter}, or {\em InPlaceLabelCollectionImageFilter}, and implementing the method {\em virtual void ThreadedGenerateData( LabelObjectType * labelObject )} in the new class. This method only has to process the labelObject passed in parameter. All the threading code and mutex lock management is already implemented. The mutex lock remain accessible if the subclass need to use it, as the {\em m\_LabelObjectContainerLock} ivar. \section{In place filtering} All the filters which are taking a {\em LabelCollectionImage} as input, and are producing a {\em LabelCollectionImage} as output, are implemented as a subclass of {\em InPlaceLabelCollectionImageFilter} and thus are running in place by default. The use can modify this behavior with the {\em SetInPlace( bool )}, {\em InPlaceOn()}, and {\em InPlaceOff()} methods, as with the usual {\em InPlaceImageFilter}. To use that feature, a developer only have to subclass {\em InPlaceLabelCollectionImageFilter} and implement the {\em virtual void ThreadedGenerateData( LabelObjectType * labelObject )}, to get easy thread support \footnote{see the previous section}, or the {\em virtual void GenerateData()} if the filter is not threadable. In that last case, the only image to manipulate is the one get with the {\em GetOutput()} method, which is the input image if the filter runs in place, or a copy of the input image if the filter is not running in place. \section{Wrappers support} All the classes provided with that article, excepted the most generic ones made to help the developer to implement some new features, can be used with WrapITK, and have been fully tested with python. % \section{Performance} % % TODO \section{Known bugs and future work} To fit the ITK style, some iterators should be implemented to be able to iterate over all the \begin{itemize} \item objects, \item lines, \item or pixels \end{itemize} of an image, starting from \begin{itemize} \item an image, \item an object, \item or a line. \end{itemize} Doing that require a good knownledge of the iterator design. Any help on that point is welcome. Also, more attributes will be implemented, like the perimeter estimation, the usual image moments, etc. It may be useful to implement the most commonly used opening, keep N objects and relabel transforms in a more efficient way, by using an {\em AttributeLabelObject} instead of a {\em ShapeLabelObject} or a {\em StatisticsLabelObject}. The computation of the moments is partially implemented. The help of someone with a good knownledge of that subject would be very helpful to complete that work. The moments for the binary shape should also be implemented. All the {\em Shape} filters are currently able to use the Feret diameter only in 2D images. For the other dimensions, the feret diameter is not computed, and so always get a value of $0$. The computation of the Feret diameter should be forced if the user want to use it. The {\em BinaryImageToLabelCollectionImageFilter} class should be threaded to get the best of that filter on multiprocessors systems. Finally, all the binary and label filters should be implemented as a subclass of {\em InPlaceImageFilter}. \section{Conclusion} ITK is currently lacking a good way to manipulate the binary objects. With that contribution I hope to have mostly filled that lack. \section{Acknowledgments} I thank Richard Beare for his suggestion to use the run length encoding to represent the binary objects, and Julien Jomier for his help for the choice to {\em not} use the {\em SpatialObject} class as base class of the {\em LabelObejct} class. I thank Dr Pierre Adenot and MIMA2 confocal facilities (\url{http://mima2.jouy.inra.fr}) for providing the 3D test image. I am grateful to the INRA MIGALE bioinformatics platform (\url{http://migale.jouy.inra.fr}) for providing the computational resources used for the timing tests. \appendix \bibliographystyle{plain} \bibliography{InsightJournal} \nocite{ITKSoftwareGuide} \end{document}