Orfeo Toolbox  3.16
ScalarImageMarkovRandomField1.cxx
Go to the documentation of this file.
1 /*=========================================================================
2 
3  Program: ORFEO Toolbox
4  Language: C++
5  Date: $Date$
6  Version: $Revision$
7 
8 
9  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
10  See OTBCopyright.txt for details.
11 
12 
13  This software is distributed WITHOUT ANY WARRANTY; without even
14  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  PURPOSE. See the above copyright notices for more information.
16 
17 =========================================================================*/
18 
19 
20 //
21 // Software Guide : BeginCommandLineArgs
22 // INPUTS: {QB_Suburb.png}, {QB_Suburb_labelled.png}
23 // OUTPUTS: {ScalarImageMarkovRandomField1Output.png}
24 // 50 3 4 79.5097 138.136 213.846 25.9395
25 // NORMALIZE_EPS_OUTPUT_OF: {ScalarImageMarkovRandomField1Output.png}
26 // Software Guide : EndCommandLineArgs
27 
28 // Software Guide : BeginLatex
29 //
30 // This example shows how to use the Markov Random Field approach for
31 // classifying the pixel of a scalar image.
32 //
33 // The \subdoxygen{itk}{Statistics}{MRFImageFilter} is used for refining an initial
34 // classification by introducing the spatial coherence of the labels. The user
35 // should provide two images as input. The first image is the one to be
36 // classified while the second image is an image of labels representing an
37 // initial classification.
38 //
39 // Software Guide : EndLatex
40 
41 // Software Guide : BeginLatex
42 //
43 // The following headers are related to reading input images, writing the
44 // output image, and making the necessary conversions between scalar and vector
45 // images.
46 //
47 // Software Guide : EndLatex
48 
49 // Software Guide : BeginCodeSnippet
50 #include "otbImage.h"
51 #include "itkFixedArray.h"
52 #include "otbImageFileReader.h"
53 #include "otbImageFileWriter.h"
55 // Software Guide : EndCodeSnippet
56 
58 
59 // Software Guide : BeginLatex
60 //
61 // The following headers are related to the statistical classification classes.
62 //
63 // Software Guide : EndLatex
64 
65 // Software Guide : BeginCodeSnippet
66 #include "itkMRFImageFilter.h"
68 #include "itkMinimumDecisionRule.h"
69 #include "itkImageClassifierBase.h"
70 // Software Guide : EndCodeSnippet
71 
72 int main(int argc, char * argv[])
73 {
74  if (argc < 5)
75  {
76  std::cerr << "Usage: " << std::endl;
77  std::cerr << argv[0];
78  std::cerr << " inputScalarImage inputLabeledImage";
79  std::cerr << " outputLabeledImage numberOfIterations";
80  std::cerr << " smoothingFactor numberOfClasses";
81  std::cerr << " mean1 mean2 ... meanN " << std::endl;
82  return EXIT_FAILURE;
83  }
84 
85  const char * inputImageFileName = argv[1];
86  const char * inputLabelImageFileName = argv[2];
87  const char * outputImageFileName = argv[3];
88 
89  const unsigned int numberOfIterations = atoi(argv[4]);
90  const double smoothingFactor = atof(argv[5]);
91  const unsigned int numberOfClasses = atoi(argv[6]);
92 
93  const unsigned int numberOfArgumentsBeforeMeans = 7;
94 
95  if (static_cast<unsigned int>(argc) <
96  numberOfClasses + numberOfArgumentsBeforeMeans)
97  {
98  std::cerr << "Error: " << std::endl;
99  std::cerr << numberOfClasses << " classes has been specified ";
100  std::cerr << "but no enough means have been provided in the command ";
101  std::cerr << "line arguments " << std::endl;
102  return EXIT_FAILURE;
103  }
104 
105 // Software Guide : BeginLatex
106 //
107 // First we define the pixel type and dimension of the image that we intend to
108 // classify. With this image type we can also declare the
109 // \doxygen{otb}{ImageFileReader} needed for reading the input image, create one and
110 // set its input filename.
111 //
112 // Software Guide : EndLatex
113 
114 // Software Guide : BeginCodeSnippet
115  typedef unsigned char PixelType;
116  const unsigned int Dimension = 2;
117 
118  typedef otb::Image<PixelType, Dimension> ImageType;
119 
120  typedef otb::ImageFileReader<ImageType> ReaderType;
121  ReaderType::Pointer reader = ReaderType::New();
122  reader->SetFileName(inputImageFileName);
123 // Software Guide : EndCodeSnippet
124 
125 // Software Guide : BeginLatex
126 //
127 // As a second step we define the pixel type and dimension of the image of
128 // labels that provides the initial classification of the pixels from the first
129 // image. This initial labeled image can be the output of a K-Means method like
130 // the one illustrated in section \ref{sec:KMeansClassifier}.
131 //
132 // Software Guide : EndLatex
133 
134 // Software Guide : BeginCodeSnippet
135  typedef unsigned char LabelPixelType;
136 
137  typedef otb::Image<LabelPixelType, Dimension> LabelImageType;
138 
139  typedef otb::ImageFileReader<LabelImageType> LabelReaderType;
140  LabelReaderType::Pointer labelReader = LabelReaderType::New();
141  labelReader->SetFileName(inputLabelImageFileName);
142 // Software Guide : EndCodeSnippet
143 
144 // Software Guide : BeginLatex
145 //
146 // Since the Markov Random Field algorithm is defined in general for images
147 // whose pixels have multiple components, that is, images of vector type, we
148 // must adapt our scalar image in order to satisfy the interface expected by
149 // the \code{MRFImageFilter}. We do this by using the
150 // \doxygen{itk}{ScalarToArrayCastImageFilter}. With this filter we will present our
151 // scalar image as a vector image whose vector pixels contain a single
152 // component.
153 //
154 // Software Guide : EndLatex
155 
156 // Software Guide : BeginCodeSnippet
157  typedef itk::FixedArray<LabelPixelType, 1> ArrayPixelType;
158 
159  typedef otb::Image<ArrayPixelType, Dimension> ArrayImageType;
160 
162  ImageType, ArrayImageType> ScalarToArrayFilterType;
163 
164  ScalarToArrayFilterType::Pointer
165  scalarToArrayFilter = ScalarToArrayFilterType::New();
166  scalarToArrayFilter->SetInput(reader->GetOutput());
167 // Software Guide : EndCodeSnippet
168 
169 // Software Guide : BeginLatex
170 //
171 // With the input image type \code{ImageType} and labeled image type
172 // \code{LabelImageType} we instantiate the type of the
173 // \doxygen{itk}{MRFImageFilter} that will apply the Markov Random Field algorithm
174 // in order to refine the pixel classification.
175 //
176 // Software Guide : EndLatex
177 
178 // Software Guide : BeginCodeSnippet
180 
181  MRFFilterType::Pointer mrfFilter = MRFFilterType::New();
182 
183  mrfFilter->SetInput(scalarToArrayFilter->GetOutput());
184 // Software Guide : EndCodeSnippet
185 
186 // Software Guide : BeginLatex
187 //
188 // We set now some of the parameters for the MRF filter. In particular, the
189 // number of classes to be used during the classification, the maximum number
190 // of iterations to be run in this filter and the error tolerance that will be
191 // used as a criterion for convergence.
192 //
193 // Software Guide : EndLatex
194 
195 // Software Guide : BeginCodeSnippet
196  mrfFilter->SetNumberOfClasses(numberOfClasses);
197  mrfFilter->SetMaximumNumberOfIterations(numberOfIterations);
198  mrfFilter->SetErrorTolerance(1e-7);
199 // Software Guide : EndCodeSnippet
200 
201 // Software Guide : BeginLatex
202 //
203 // The smoothing factor represents the tradeoff between fidelity to the
204 // observed image and the smoothness of the segmented image. Typical smoothing
205 // factors have values between 1~5. This factor will multiply the weights that
206 // define the influence of neighbors on the classification of a given pixel.
207 // The higher the value, the more uniform will be the regions resulting from
208 // the classification refinement.
209 //
210 // Software Guide : EndLatex
211 
212 // Software Guide : BeginCodeSnippet
213  mrfFilter->SetSmoothingFactor(smoothingFactor);
214 // Software Guide : EndCodeSnippet
215 
216 // Software Guide : BeginLatex
217 //
218 // Given that the MRF filter needs to continually relabel the pixels, it needs
219 // access to a set of membership functions that will measure to what degree
220 // every pixel belongs to a particular class. The classification is performed
221 // by the \doxygen{itk}{ImageClassifierBase} class, that is instantiated using the
222 // type of the input vector image and the type of the labeled image.
223 //
224 // Software Guide : EndLatex
225 
226 // Software Guide : BeginCodeSnippet
227  typedef itk::ImageClassifierBase<
228  ArrayImageType,
229  LabelImageType> SupervisedClassifierType;
230 
231  SupervisedClassifierType::Pointer classifier =
232  SupervisedClassifierType::New();
233 // Software Guide : EndCodeSnippet
234 
235 // Software Guide : BeginLatex
236 //
237 // The classifier needs a decision rule to be set by the user. Note
238 // that we must use \code{GetPointer()} in the call of the
239 // \code{SetDecisionRule()} method because we are passing a
240 // SmartPointer, and smart pointer cannot perform polymorphism, we
241 // must then extract the raw pointer that is associated to the smart
242 // pointer. This extraction is done with the GetPointer() method.
243 //
244 // Software Guide : EndLatex
245 
246 // Software Guide : BeginCodeSnippet
247  typedef itk::MinimumDecisionRule DecisionRuleType;
248 
249  DecisionRuleType::Pointer classifierDecisionRule = DecisionRuleType::New();
250 
251  classifier->SetDecisionRule(classifierDecisionRule.GetPointer());
252 // Software Guide : EndCodeSnippet
253 
254 // Software Guide : BeginLatex
255 //
256 // We now instantiate the membership functions. In this case we use the
257 // \subdoxygen{itk}{Statistics}{DistanceToCentroidMembershipFunction} class
258 // templated over the pixel type of the vector image, which in our example
259 // happens to be a vector of dimension 1.
260 //
261 // Software Guide : EndLatex
262 
263 // Software Guide : BeginCodeSnippet
265  ArrayPixelType>
266  MembershipFunctionType;
267 
268  typedef MembershipFunctionType::Pointer MembershipFunctionPointer;
269 
270  double meanDistance = 0;
271  vnl_vector<double> centroid(1);
272  for (unsigned int i = 0; i < numberOfClasses; ++i)
273  {
274  MembershipFunctionPointer membershipFunction =
275  MembershipFunctionType::New();
276 
277  centroid[0] = atof(argv[i + numberOfArgumentsBeforeMeans]);
278 
279  membershipFunction->SetCentroid(centroid);
280 
281  classifier->AddMembershipFunction(membershipFunction);
282  meanDistance += static_cast<double> (centroid[0]);
283  }
284  meanDistance /= numberOfClasses;
285 // Software Guide : EndCodeSnippet
286 
287 // Software Guide : BeginLatex
288 //
289 // and we set the neighborhood radius that will define the size of the clique
290 // to be used in the computation of the neighbors' influence in the
291 // classification of any given pixel. Note that despite the fact that we call
292 // this a radius, it is actually the half size of an hypercube. That is, the
293 // actual region of influence will not be circular but rather an N-Dimensional
294 // box. For example, a neighborhood radius of 2 in a 3D image will result in a
295 // clique of size 5x5x5 pixels, and a radius of 1 will result in a clique of
296 // size 3x3x3 pixels.
297 //
298 // Software Guide : EndLatex
299 
300 // Software Guide : BeginCodeSnippet
301  mrfFilter->SetNeighborhoodRadius(1);
302 // Software Guide : EndCodeSnippet
303 
304 // Software Guide : BeginLatex
305 //
306 // We should now set the weights used for the neighbors. This is done by
307 // passing an array of values that contains the linear sequence of weights for
308 // the neighbors. For example, in a neighborhood of size 3x3x3, we should
309 // provide a linear array of 9 weight values. The values are packaged in a
310 // \code{std::vector} and are supposed to be \code{double}. The following lines
311 // illustrate a typical set of values for a 3x3x3 neighborhood. The array is
312 // arranged and then passed to the filter by using the method
313 // \code{SetMRFNeighborhoodWeight()}.
314 //
315 // Software Guide : EndLatex
316 
317 // Software Guide : BeginCodeSnippet
318  std::vector<double> weights;
319  weights.push_back(1.5);
320  weights.push_back(2.0);
321  weights.push_back(1.5);
322  weights.push_back(2.0);
323  weights.push_back(0.0); // This is the central pixel
324  weights.push_back(2.0);
325  weights.push_back(1.5);
326  weights.push_back(2.0);
327  weights.push_back(1.5);
328 // Software Guide : EndCodeSnippet
329 
330 // Software Guide : BeginLatex
331 // We now scale weights so that the smoothing function and the image fidelity
332 // functions have comparable value. This is necessary since the label
333 // image and the input image can have different dynamic ranges. The fidelity
334 // function is usually computed using a distance function, such as the
335 // \doxygen{itk}{DistanceToCentroidMembershipFunction} or one of the other
336 // membership functions. They tend to have values in the order of the means
337 // specified.
338 // Software Guide : EndLatex
339 
340 // Software Guide : BeginCodeSnippet
341  double totalWeight = 0;
342  for (std::vector<double>::const_iterator wcIt = weights.begin();
343  wcIt != weights.end(); ++wcIt)
344  {
345  totalWeight += *wcIt;
346  }
347  for (std::vector<double>::iterator wIt = weights.begin();
348  wIt != weights.end(); wIt++)
349  {
350  *wIt = static_cast<double> ((*wIt) * meanDistance / (2 * totalWeight));
351  }
352 
353  mrfFilter->SetMRFNeighborhoodWeight(weights);
354 // Software Guide : EndCodeSnippet
355 
356 // Software Guide : BeginLatex
357 //
358 // Finally, the classifier class is connected to the Markov Random Fields filter.
359 //
360 // Software Guide : EndLatex
361 
362 // Software Guide : BeginCodeSnippet
363  mrfFilter->SetClassifier(classifier);
364 // Software Guide : EndCodeSnippet
365 
366 // Software Guide : BeginLatex
367 //
368 // The output image produced by the \doxygen{itk}{MRFImageFilter} has the same pixel
369 // type as the labeled input image. In the following lines we use the
370 // \code{OutputImageType} in order to instantiate the type of a
371 // \doxygen{otb}{ImageFileWriter}. Then create one, and connect it to the output of
372 // the classification filter after passing it through an intensity rescaler
373 // to rescale it to an 8 bit dynamic range
374 //
375 // Software Guide : EndLatex
376 // Software Guide : BeginCodeSnippet
377  typedef MRFFilterType::OutputImageType OutputImageType;
378 // Software Guide : EndCodeSnippet
379 
380  // Rescale outputs to the dynamic range of the display
381  typedef otb::Image<unsigned char, Dimension> RescaledOutputImageType;
383  OutputImageType, RescaledOutputImageType> RescalerType;
384 
385  RescalerType::Pointer intensityRescaler = RescalerType::New();
386  intensityRescaler->SetOutputMinimum(0);
387  intensityRescaler->SetOutputMaximum(255);
388  intensityRescaler->SetInput(mrfFilter->GetOutput());
389 
390 // Software Guide : BeginCodeSnippet
391  typedef otb::ImageFileWriter<OutputImageType> WriterType;
392 
393  WriterType::Pointer writer = WriterType::New();
394 
395  writer->SetInput(intensityRescaler->GetOutput());
396 
397  writer->SetFileName(outputImageFileName);
398 // Software Guide : EndCodeSnippet
399 
400 // Software Guide : BeginLatex
401 //
402 // We are now ready for triggering the execution of the pipeline. This is done
403 // by simply invoking the \code{Update()} method in the writer. This call will
404 // propagate the update request to the reader and then to the MRF filter.
405 //
406 // Software Guide : EndLatex
407 
408 // Software Guide : BeginCodeSnippet
409  try
410  {
411  writer->Update();
412  }
413  catch (itk::ExceptionObject& excp)
414  {
415  std::cerr << "Problem encountered while writing ";
416  std::cerr << " image file : " << argv[2] << std::endl;
417  std::cerr << excp << std::endl;
418  return EXIT_FAILURE;
419  }
420 // Software Guide : EndCodeSnippet
421 
422  std::cout << "Number of Iterations : ";
423  std::cout << mrfFilter->GetNumberOfIterations() << std::endl;
424  std::cout << "Stop condition: " << std::endl;
425  std::cout << " (1) Maximum number of iterations " << std::endl;
426  std::cout << " (2) Error tolerance: " << std::endl;
427  std::cout << mrfFilter->GetStopCondition() << std::endl;
428 
429  // Software Guide : BeginLatex
430  //
431  // \begin{figure} \center
432  // \includegraphics[width=0.44\textwidth]{ScalarImageMarkovRandomField1Output.eps}
433  // \itkcaption[Output of the ScalarImageMarkovRandomField]{Effect of the
434  // MRF filter.}
435  // \label{fig:ScalarImageMarkovRandomFieldInputOutput}
436  // \end{figure}
437  //
438  // Figure \ref{fig:ScalarImageMarkovRandomFieldInputOutput}
439  // illustrates the effect of this filter with four classes.
440  // In this example the filter was run with a smoothing factor of 3.
441  // The labeled image was produced by ScalarImageKmeansClassifier.cxx
442  // and the means were estimated by
443  // ScalarImageKmeansModelEstimator.cxx described in section
444  // \ref{sec:KMeansClassifier}.
445  // The obtained result can be compared with the one of
446  // figure~\ref{fig:ScalarImageKMeansClassifierOutput} to see the
447  // interest of using the MRF approach in order to ensure the
448  // regularization of the classified image.
449  //
450  // Software Guide : EndLatex
451 
452  return EXIT_SUCCESS;
453 
454 }

Generated at Sun Feb 3 2013 00:59:33 for Orfeo Toolbox with doxygen 1.8.1.1