All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
ClassifierISOSegStrategy.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2008 National Institute For Space Research (INPE) - Brazil.
2 
3  This file is part of the TerraLib - a Framework for building GIS enabled applications.
4 
5  TerraLib is free software: you can redistribute it and/or modify
6  it under the terms of the GNU Lesser General Public License as published by
7  the Free Software Foundation, either version 3 of the License,
8  or (at your option) any later version.
9 
10  TerraLib is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public License
16  along with TerraLib. See COPYING. If not, write to
17  TerraLib Team at <terralib-team@terralib.org>.
18  */
19 
20 /*!
21  \file terralib/rp/ClassifierISOSegStrategy.cpp
22 
23  \brief Raster ISOSeg strategy for segmentation-based classification.
24 */
25 
26 // TerraLib
27 #include "../common/MatrixUtils.h"
28 #include "../common/progress/TaskProgress.h"
29 #include "../geometry/Coord2D.h"
30 #include "../geometry/Envelope.h"
31 #include "../geometry/Point.h"
32 #include "../geometry/Polygon.h"
33 #include "../raster/Grid.h"
34 #include "../raster/PositionIterator.h"
35 #include "../raster/Utils.h"
37 #include "Functions.h"
38 #include "Macros.h"
39 #include "RasterAttributes.h"
40 
41 // STL
42 #include <iostream>
43 #include <set>
44 #include <stdlib.h>
45 
46 // Boost
47 #include <boost/numeric/ublas/io.hpp>
48 #include <boost/numeric/ublas/lu.hpp>
49 #include <boost/numeric/ublas/matrix.hpp>
50 
51 // STL
52 #include <cfloat>
53 
54 namespace
55 {
56  static te::rp::ClassifierISOSegStrategyFactory classifierISOSegStrategyFactoryInstance;
57 }
58 
60 {
61  reset();
62 }
63 
65 {
66 }
67 
69 {
70  reset();
71 
72  m_acceptanceThreshold = rhs.m_acceptanceThreshold;
73 
74  return *this;
75 }
76 
78 {
79  m_acceptanceThreshold = 0;
80 }
81 
83 {
85 }
86 
88 {
89  m_id = -1;
90 
91  m_myCluster = 0;
92 
93  m_area = 0.0;
94 }
95 
96 te::rp::ClassifierISOSegStrategy::Pattern::Pattern(int i, double a, std::vector<double> mv, boost::numeric::ublas::matrix<double> cm)
97  : m_id(i),
98  m_myCluster(0),
99  m_area(a),
100  m_meanVector(mv),
101  m_covarianceMatrix(cm)
102 {
103  m_covarianceInversion = boost::numeric::ublas::matrix<double>(m_covarianceMatrix.size1(), m_covarianceMatrix.size2());
104 
106 }
107 
109  : m_id(rhs.m_id),
110  m_myCluster(rhs.m_myCluster),
111  m_area(rhs.m_area)
112 {
114 
116 
118 }
119 
121 {
122  m_id = -1;
123 
124  m_myCluster = 0;
125 
126  m_area = 0.0;
127 
128  m_meanVector.clear();
129 
130  m_covarianceMatrix.clear();
131 
132  m_covarianceInversion.clear();
133 }
134 
136 {
137  assert(m_meanVector.size() == p->m_meanVector.size());
138  assert(p->m_area > 0.0);
139  assert(m_myCluster == 0);
140 
141 // update mean vectors
142  double total_area = m_area + p->m_area;
143  for (unsigned i = 0; i < m_meanVector.size(); i++)
144  m_meanVector[i] = (m_meanVector[i] * m_area + p->m_meanVector[i] * p->m_area) / total_area;
145 
146 // update covariance matrices (the matrix with the biggest area wins)
147  if (p->m_area > m_area)
148  {
149  m_covarianceMatrix = p->m_covarianceMatrix;
150 
151  m_covarianceInversion = p->m_covarianceInversion;
152  }
153 
154 // add the new area
155  m_area += p->m_area;
156 
157 // update the pattern values
158  p->m_myCluster = this;
159 }
160 
162 {
163  assert(m_meanVector.size() == p->m_meanVector.size());
164 
165  unsigned int nBands = m_meanVector.size();
166 
167  boost::numeric::ublas::matrix<double> term1(1, nBands);
168  boost::numeric::ublas::matrix<double> term2(nBands, 1);
169 
170  for (unsigned int i = 0; i < nBands; i++)
171  {
172  term1(0, i) = m_meanVector[i] - p->m_meanVector[i];
173 
174  term2(i, 0) = term1(0, i);
175  }
176 
177  term1 = prod(term1, m_covarianceInversion);
178  term1 = prod(term1, term2);
179 
180  if (term1(0, 0) < 0)
181  return DBL_MAX;
182 
183  return term1(0, 0);
184 }
185 
187 {
188  assert(m_myCluster == 0);
189  assert(rhs.m_myCluster == 0);
190 
191  return m_id == rhs.m_id;
192 }
193 
195 {
196  m_isInitialized = false;
197 }
198 
200 {
201 }
202 
203 bool te::rp::ClassifierISOSegStrategy::initialize(te::rp::StrategyParameters const* const strategyParams) throw(te::rp::Exception)
204 {
205  m_isInitialized = false;
206 
207  te::rp::ClassifierISOSegStrategy::Parameters const* paramsPtr = dynamic_cast<te::rp::ClassifierISOSegStrategy::Parameters const*>(strategyParams);
208 
209  if(!paramsPtr)
210  return false;
211 
212  m_parameters = *(paramsPtr);
213 
214  TERP_TRUE_OR_RETURN_FALSE(m_parameters.m_acceptanceThreshold > 0 && m_parameters.m_acceptanceThreshold <= 100, "Invalid acceptance threshold [0, 100].")
215 
216  m_isInitialized = true;
217 
218  return true;
219 }
220 
221 double getThreshold(double acceptanceThreshold, unsigned nBands)
222 {
223  const double dmax = std::numeric_limits<double>::max();
224  double chiTable[10][6] =
225  {
226 // 75.0% 90.0% 95.0% 99.0% 99.9% 100.%
227  { 1.32, 2.71, 3.84, 6.64, 10.83, dmax},
228  { 2.77, 4.61, 5.99, 9.21, 13.82, dmax},
229  { 4.11, 6.25, 7.82, 11.35, 16.27, dmax},
230  { 5.39, 7.78, 9.49, 13.28, 18.47, dmax},
231  { 6.63, 9.24, 11.07, 15.09, 20.52, dmax},
232  { 7.84, 10.65, 12.59, 16.81, 22.46, dmax},
233  { 9.04, 12.02, 14.07, 18.48, 24.32, dmax},
234  {10.22, 13.36, 15.51, 20.09, 26.13, dmax},
235  {11.39, 14.68, 16.92, 21.67, 27.88, dmax},
236  {12.55, 15.99, 18.31, 23.21, 29.59, dmax}
237  };
238 
239  unsigned it;
240  if (nBands > 6)
241  nBands = 6;
242 
243  if(acceptanceThreshold < 90.0)
244  it = 0;
245  else if(acceptanceThreshold < 95.0)
246  it = 1;
247  else if(acceptanceThreshold < 99.0)
248  it = 2;
249  else if(acceptanceThreshold < 99.9)
250  it = 3;
251  else if(acceptanceThreshold < 100.0)
252  it = 4;
253  else
254  it = 5;
255 
256  return chiTable[nBands-1][it];
257 }
258 
259 bool te::rp::ClassifierISOSegStrategy::execute(const te::rst::Raster& inputRaster, const std::vector<unsigned int>& inputRasterBands,
260  const std::vector<te::gm::Polygon*>& inputPolygons, te::rst::Raster& outputRaster,
261  const unsigned int outputRasterBand, const bool enableProgressInterface) throw(te::rp::Exception)
262 {
263  TERP_TRUE_OR_RETURN_FALSE(m_isInitialized, "Instance not initialized")
264  TERP_TRUE_OR_RETURN_FALSE(inputPolygons.size() > 0, "ISOSeg algorithm needs polygons")
265 
266  te::rp::RasterAttributes rattributes;
267 
268 // fill m_regions, in the beginning, each region is a cluster
269  te::common::TaskProgress task_fx(TE_TR("ISOSeg algorithm - feature extraction"), te::common::TaskProgress::UNDEFINED, inputPolygons.size());
270  for (unsigned i = 0; i < inputPolygons.size(); i++)
271  {
272  te::gm::Polygon* polygon = inputPolygons[i];
273 
274  std::vector<std::vector<double> > values_in_polygon = rattributes.getValuesFromRaster(inputRaster, *polygon, inputRasterBands);
275  std::vector<double> means;
276  for (unsigned int b = 0; b < values_in_polygon.size(); b++)
277  {
278  te::stat::NumericStatisticalSummary summary = rattributes.getStatistics(values_in_polygon[b]);
279  means.push_back(summary.m_mean);
280  }
281 
282  Pattern* region = new Pattern(i, polygon->getArea(), means, rattributes.getCovarianceMatrix(values_in_polygon, means));
283 
284  Pattern* cluster = new Pattern(*region);
285 
286  region->m_myCluster = cluster;
287 
288  m_regions.insert(std::pair<double, Pattern*> (region->m_area, region));
289 
290  task_fx.pulse();
291  }
292 
293  double distance;
294  double distance2;
295 
296 // loops until all the regions are classified
297  std::multimap<double, Pattern*, std::greater<double> >::iterator rit;
298  std::multimap<double, Pattern*, std::greater<double> >::iterator riti;
299  std::multimap<double, Pattern*, std::greater<double> >::iterator ritii;
300 
301 // merge similar clusters
302  bool stable = false;
303  int oldid;
304  std::set<std::pair<unsigned int, unsigned int> > compared;
305 
306  te::common::TaskProgress task_clustering(TE_TR("ISOSeg algorithm - detecting clusters"));
307  double maxDistance = getThreshold(m_parameters.m_acceptanceThreshold, inputRasterBands.size());
308  while (!stable)
309  {
310  stable = true;
311 
312  compared.clear();
313 
314 // analyze all regions
315  for (rit = m_regions.begin(); rit != m_regions.end(); ++rit)
316  {
317 // compare all clusters to detect if they are still similar
318  for (riti = rit; riti != m_regions.end(); ++riti)
319  {
320  if (compared.find(std::pair<unsigned int, unsigned int>(rit->second->m_myCluster->m_id, riti->second->m_myCluster->m_id)) == compared.end())
321  {
322  compared.insert(std::pair<unsigned int, unsigned int>(rit->second->m_myCluster->m_id, riti->second->m_myCluster->m_id));
323  compared.insert(std::pair<unsigned int, unsigned int>(riti->second->m_myCluster->m_id, rit->second->m_myCluster->m_id));
324  }
325  else
326  continue;
327 
328  if (rit->second->m_myCluster == riti->second->m_myCluster)
329  continue;
330 
331 // use the smallest distance between region1 <-> cluster2, and region2 <-> cluster1
332  distance = rit->second->m_myCluster->getDistance(riti->second->m_myCluster);
333 
334  distance2 = riti->second->m_myCluster->getDistance(rit->second->m_myCluster);
335 
336  distance = (distance2 < distance? distance2: distance);
337 
338  if (distance <= maxDistance)
339  {
340 // two clusters are similar and must be merged
341  stable = false;
342 
343  oldid = riti->second->m_myCluster->m_id;
344 
345  for (ritii = riti; ritii != m_regions.end(); ++ritii)
346  {
347 // find all clusters with riti class and merge with rit cluster
348  if (ritii->second->m_myCluster->m_id != oldid)
349  continue;
350 
351  ritii->second->m_myCluster = rit->second->m_myCluster;
352  }
353  }
354  }
355  }
356  task_clustering.pulse();
357  }
358 
359 // remap cluster values to 1 -> N
360  std::set<unsigned int> ulabels;
361  std::set<unsigned int>::iterator lit;
362  std::map<unsigned int, unsigned int> colormap;
363 
364  for (rit = m_regions.begin(); rit != m_regions.end(); ++rit)
365  ulabels.insert(rit->second->m_myCluster->m_id);
366 
367  unsigned int color = 1;
368  for (lit = ulabels.begin(); lit != ulabels.end(); ++lit)
369  colormap[*lit] = color++;
370 
371  te::gm::Coord2D geoCoord;
372  unsigned int pattern;
373 
374 // classify output image
375  te::common::TaskProgress task(TE_TR("ISOSeg algorithm - classifying image"), te::common::TaskProgress::UNDEFINED, m_regions.size());
376  for (rit = m_regions.begin(); rit != m_regions.end(); ++rit)
377  {
378  te::gm::Polygon* polygon = inputPolygons[rit->second->m_id];
379 
380  pattern = rit->second->m_myCluster->m_id;
381 
382 // iterate over polygon to classify output image
385 
386  while (it != itend)
387  {
388  outputRaster.setValue(it.getColumn(), it.getRow(), colormap[pattern], outputRasterBand);
389 
390  ++it;
391  }
392  task.pulse();
393  }
394 
395  return true;
396 }
397 
399  : te::rp::ClassifierStrategyFactory("isoseg")
400 {
401 }
402 
404 {
405 }
406 
408 {
410 }
A structure to hold the set of statistics from a set of numerical values.
Raster ISOSeg Classifier strategy factory.
bool operator=(Pattern &rhs)
Return true if two clusters are equal.
AbstractParameters * clone() const
Create a clone copy of this instance.
double getDistance(Pattern *p)
Returns the Mahalanobis distance between two patterns.
This class implements the strategy to iterate with spatial restriction, the iteration occurs inside a...
boost::numeric::ublas::matrix< double > m_covarianceInversion
The inversion of covariance matrix between bands.
Extraction of attributes from Raster, Bands, and Polygons.
This class can be used to inform the progress of a task.
Definition: TaskProgress.h:53
Defines the exact sequence of characters that are acceptable.
Definition: Enums.h:102
void add(Pattern *p)
Add a region inside a cluster.
unsigned int getRow() const
Returns the current row in iterator.
An utility struct for representing 2D coordinates.
Definition: Coord2D.h:40
std::multimap< double, Pattern *, std::greater< double > > m_regions
A descriptive set of regions (area, features).
#define TE_TR(message)
It marks a string in order to get translated.
Definition: Translator.h:347
bool m_isInitialized
True if this instance is initialized.
bool initialize(StrategyParameters const *const strategyParams)
Initialize the classification strategy.
Extraction of attributes from Raster, Bands, and Polygons.
Raster Processing functions.
#define TERP_TRUE_OR_RETURN_FALSE(value, message)
Checks if value is true. For false values a warning message will be logged and a return of context wi...
Definition: Macros.h:183
Raster classifier strategy factory base class.
ISOSeg strategy for OBIA classification. The algorithm orders regions by area (larger first)...
const Parameters & operator=(const Parameters &params)
static PolygonIterator end(const te::rst::Raster *r, const te::gm::Polygon *p)
Returns an iterator referring to after the end of the iterator.
An abstract class for raster data strucutures.
Definition: Raster.h:71
Raster strategy parameters base class.
void pulse()
Calls setCurrentStep() function using getCurrentStep() + 1.
double getArea() const
It returns the area of this surface, as measured in the spatial reference system of this surface...
int m_id
The id of the region of the pattern.
te::rp::ClassifierStrategy * build()
Concrete factories (derived from this one) must implement this method in order to create objects...
Abstract parameters base interface.
Polygon is a subclass of CurvePolygon whose rings are defined by linear rings.
Definition: Polygon.h:50
Describes a region or a cluster (group of regions with similar properties) to be used by ISOSeg metho...
static PolygonIterator begin(const te::rst::Raster *r, const te::gm::Polygon *p)
Returns an iterator referring to the first value of the band.
Raster classifier strategy base class.
bool GetInverseMatrix(const boost::numeric::ublas::matrix< T > &inputMatrix, boost::numeric::ublas::matrix< T > &outputMatrix)
Matrix inversion.
Definition: MatrixUtils.h:104
ClassifierISOSegStrategy::Parameters m_parameters
Internal execution parameters.
double m_area
The area of all regions inside a pattern.
std::vector< double > m_meanVector
The vector of mean values, 1 value per band;.
bool execute(const te::rst::Raster &inputRaster, const std::vector< unsigned int > &inputRasterBands, const std::vector< te::gm::Polygon * > &inputPolygons, te::rst::Raster &outputRaster, const unsigned int outputRasterBand, const bool enableProgressInterface)
Executes the classification strategy.
double m_acceptanceThreshold
The acceptance threshold (the closer to 100%, few clusters are created).
double getThreshold(double acceptanceThreshold, unsigned nBands)
ISOSeg strategy for segmentation-based classification.
unsigned int getColumn() const
Returns the current column in iterator.
Pattern * m_myCluster
The associated cluster of this pattern (optional).
void reset()
Clear all internal allocated resources and reset the parameters instance to its initial state...
boost::numeric::ublas::matrix< double > m_covarianceMatrix
The covariance matrix between bands.