File: SEMModelEstimatorExample.cxx

package info (click to toggle)
otb 8.1.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,030,436 kB
  • sloc: xml: 231,007; cpp: 224,490; ansic: 4,592; sh: 1,790; python: 1,131; perl: 92; makefile: 72
file content (218 lines) | stat: -rw-r--r-- 8,058 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/*
 * Copyright (C) 2005-2022 Centre National d'Etudes Spatiales (CNES)
 * Copyright (C) 2007-2012 Institut Mines Telecom / Telecom Bretagne
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


/* Example usage:
./SEMModelEstimatorExample Input/ROI_QB_MUL_4.tif Output/SEMClassif.png 4 40 5
*/

//
// In this example, we present OTB's implementation of SEM, through the class
// \doxygen{otb}{SEMClassifier}. This class performs a stochastic version
// of the EM algorithm, but instead of inheriting from
// \doxygen{itk}{ExpectationMaximizationMixtureModelEstimator}, we chose to
// inherit from \subdoxygen{itk}{Statistics}{ListSample},
// in the same way as \doxygen{otb}{SVMClassifier}.
//
// The program begins with \doxygen{otb}{VectorImage} and outputs
// \doxygen{otb}{Image}. Then appropriate header files have to be included:

#include <iostream>

#include "itkVector.h"
#include "itkVariableLengthVector.h"
#include "itkUnaryFunctorImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"

#include "otbImage.h"
#include "otbVectorImage.h"
#include "otbImageFileReader.h"
#include "otbImageFileWriter.h"

// \doxygen{otb}{SEMClassifier} performs estimation of mixture to fit the
// initial histogram. Actually, mixture of Gaussian pdf can be performed.
// Those generic pdf are treated in
// \subdoxygen{otb}{Statistics}{ModelComponentBase}. The Gaussian model
// is taken in charge with the class
// \subdoxygen{otb}{Statistics}{GaussianModelComponent}.

#include "otbSEMClassifier.h"

int main(int argc, char* argv[])
{
  try
  {

    if (argc != 6)
    {
      std::cerr << "Unsupervised Image Segmentation with SEM approach\n";
      std::cerr << argv[0] << " imageIn imgClassif num_of_class ";
      std::cerr << "nbIteration size_of_the_neighborhood\n";
      return EXIT_FAILURE;
    }

    // Input/Output images type are define in a classical way.
    // In fact, a \doxygen{itk}{VariableLengthVector} is to be
    // considered for the templated \code{MeasurementVectorType}, which
    // will be used in the \code{ListSample} interface.

    using PixelType = double;

    using ImageType  = otb::VectorImage<PixelType, 2>;
    using ReaderType = otb::ImageFileReader<ImageType>;

    using OutputImageType = otb::Image<unsigned char, 2>;
    using WriterType      = otb::ImageFileWriter<OutputImageType>;

    char*  fileNameIn           = argv[1];
    char*  fileNameImgInit      = nullptr;
    char*  fileNameOut          = argv[2];
    int    numberOfClasses      = atoi(argv[3]);
    int    numberOfIteration    = atoi(argv[4]);
    int    neighborhood         = atoi(argv[5]);
    double terminationThreshold = 1e-5;

    ReaderType::Pointer reader = ReaderType::New();
    reader->SetFileName(fileNameIn);
    reader->Update();

    // Once the input image is opened, the classifier may be initialised by
    // \code{SmartPointer}.

    using ClassifType               = otb::SEMClassifier<ImageType, OutputImageType>;
    ClassifType::Pointer classifier = ClassifType::New();

    //  Then, it follows, classical initializations of the pipeline.

    classifier->SetNumberOfClasses(numberOfClasses);
    classifier->SetMaximumIteration(numberOfIteration);
    classifier->SetNeighborhood(neighborhood);
    classifier->SetTerminationThreshold(terminationThreshold);
    classifier->SetSample(reader->GetOutput());

    // When an initial segmentation is available, the classifier may use it
    // as image (of type \code{OutputImageType}) or as a
    // \doxygen{itk}{SampleClassifier} result (of type
    // \subdoxygen{itk}{Statistics}{MembershipSample}).

    if (fileNameImgInit != nullptr)
    {
      using ImgInitReaderType              = otb::ImageFileReader<OutputImageType>;
      ImgInitReaderType::Pointer segReader = ImgInitReaderType::New();
      segReader->SetFileName(fileNameImgInit);
      segReader->Update();
      classifier->SetClassLabels(segReader->GetOutput());
    }

    //  By default, \doxygen{otb}{SEMClassifier} performs initialization of
    // \code{ModelComponentBase} by as many instantiation of
    // \subdoxygen{otb}{Statistics}{GaussianModelComponent} as the number of
    // classes to estimate in the mixture. Nevertheless, the user may add specific
    // distribution into the mixture estimation. It is permitted by the use of
    // \code{AddComponent} for the given class number and the specific distribution.

    std::cerr << "Explicit component initialization\n";

    using ClassSampleType = ClassifType::ClassSampleType;
    using GaussianType    = otb::Statistics::GaussianModelComponent<ClassSampleType>;

    for (int i = 0; i < numberOfClasses; ++i)
    {
      GaussianType::Pointer model = GaussianType::New();
      classifier->AddComponent(i, model);
    }

    //  Once the pipeline is instantiated. The segmentation by itself may be
    // launched by using the \code{Update} function.

    try
    {
      classifier->Update();
    }

    catch (itk::ExceptionObject& err)
    {
      std::cerr << "ExceptionObject caught in " << argv[0] << "!\n";
      std::cerr << err << std::endl;
      return -1;
    }

    //  The segmentation may outputs a result of type
    // \subdoxygen{itk}{Statistics}{MembershipSample} as it is the
    // case for the \doxygen{otb}{SVMClassifier}. But when using
    // \code{GetOutputImage} the output is directly an Image.
    //
    // Only for visualization purposes, we choose to rescale the image of
    // classes before saving it to a file. We will use the
    // \doxygen{itk}{RescaleIntensityImageFilter} for this purpose.

    using RescalerType             = itk::RescaleIntensityImageFilter<OutputImageType, OutputImageType>;
    RescalerType::Pointer rescaler = RescalerType::New();

    rescaler->SetOutputMinimum(itk::NumericTraits<unsigned char>::min());
    rescaler->SetOutputMaximum(itk::NumericTraits<unsigned char>::max());

    rescaler->SetInput(classifier->GetOutputImage());

    WriterType::Pointer writer = WriterType::New();
    writer->SetFileName(fileNameOut);
    writer->SetInput(rescaler->GetOutput());
    writer->Update();

    // Figure \ref{fig:RESSEMCLASSIF} shows the result of the SEM segmentation
    // with 4 different classes and a contextual neighborhood of 3 pixels.
    // \begin{figure}
    //  \center
    //  \includegraphics[width=0.6\textwidth]{SEMClassif.eps}
    //  \itkcaption[SEM Classification results]{SEM Classification results.}
    //  \label{fig:RESSEMCLASSIF}
    // \end{figure}
    //
    // As soon as the segmentation is performed by an iterative stochastic
    // process, it is worth verifying the output status: does the segmentation
    // ends when it has converged or just at the limit of the iteration numbers.

    std::cerr << "Program terminated with a ";
    if (classifier->GetTerminationCode() == ClassifType::CONVERGED)
      std::cerr << "converged ";
    else
      std::cerr << "not-converged ";
    std::cerr << "code...\n";

    //  The text output gives for each class the parameters of the pdf (e.g. mean
    // of each component of the class and there covariance matrix, in the case of a
    // Gaussian mixture model).

    classifier->Print(std::cerr);
  }
  catch (itk::ExceptionObject& err)
  {
    std::cerr << "Exception itk::ExceptionObject thrown !\n";
    std::cerr << err << std::endl;
    return EXIT_FAILURE;
  }
  catch (...)
  {
    std::cerr << "Unknown exception thrown !\n";
    return EXIT_FAILURE;
  }
  return EXIT_SUCCESS;
}