All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ClassifierISOSegStrategy.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2001-2009 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 <complex>
43 #include <iostream>
44 #include <set>
45 #include <stdlib.h>
46 
47 // Boost
48 #include <boost/numeric/ublas/io.hpp>
49 #include <boost/numeric/ublas/lu.hpp>
50 #include <boost/numeric/ublas/matrix.hpp>
51 
52 // STL
53 #include <cfloat>
54 
55 namespace
56 {
57  static te::rp::ClassifierISOSegStrategyFactory classifierISOSegStrategyFactoryInstance;
58 }
59 
61 {
62  reset();
63 }
64 
66 {
67 }
68 
70 {
71  reset();
72 
73  m_acceptanceThreshold = rhs.m_acceptanceThreshold;
74 
75  return *this;
76 }
77 
79 {
80  m_acceptanceThreshold = 0;
81 }
82 
84 {
86 }
87 
89 {
90  m_id = -1;
91 
92  m_myCluster = 0;
93 
94  m_area = 0.0;
95 }
96 
97 te::rp::ClassifierISOSegStrategy::Pattern::Pattern(int i, double a, std::vector<std::complex<double> > mv, boost::numeric::ublas::matrix<double> cm)
98  : m_id(i),
99  m_myCluster(0),
100  m_area(a),
101  m_meanVector(mv),
102  m_covarianceMatrix(cm)
103 {
104  m_covarianceInversion = boost::numeric::ublas::matrix<double>(m_covarianceMatrix.size1(), m_covarianceMatrix.size2());
105 
107 }
108 
110  : m_id(rhs.m_id),
111  m_myCluster(rhs.m_myCluster),
112  m_area(rhs.m_area)
113 {
115 
117 
119 }
120 
122 {
123  m_id = -1;
124 
125  m_myCluster = 0;
126 
127  m_area = 0.0;
128 
129  m_meanVector.clear();
130 
131  m_covarianceMatrix.clear();
132 
133  m_covarianceInversion.clear();
134 }
135 
137 {
138  assert(m_meanVector.size() == p->m_meanVector.size());
139  assert(p->m_area > 0.0);
140  assert(m_myCluster == 0);
141 
142 // update mean vectors
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) / (m_area + p->m_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].real() - p->m_meanVector[i].real();
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  std::complex<double> mean;
267 
268  te::rp::RasterAttributes rattributes;
269 
270 // fill m_regions, in the beginning, each region is a cluster
271  for (unsigned i = 0; i < inputPolygons.size(); i++)
272  {
273  te::gm::Polygon* polygon = inputPolygons[i];
274 
275  std::vector<std::complex<double> > means = rattributes.getMeans(inputRaster, *polygon, inputRasterBands);
276 
277  Pattern* region = new Pattern(i, polygon->getArea(), means, rattributes.getCovarianceMatrix(inputRaster, *polygon, inputRasterBands));
278 
279  Pattern* cluster = new Pattern(*region);
280 
281  region->m_myCluster = cluster;
282 
283  m_regions.insert(std::pair<double, Pattern*> (region->m_area, region));
284  }
285 
286  double distance;
287  double distance2;
288 
289 // loops until all the regions are classified
290  std::multimap<double, Pattern*, std::greater<double> >::iterator rit;
291  std::multimap<double, Pattern*, std::greater<double> >::iterator riti;
292  std::multimap<double, Pattern*, std::greater<double> >::iterator ritii;
293 
294 // merge similar clusters
295  bool stable = false;
296  int oldid;
297  std::set<std::pair<unsigned int, unsigned int> > compared;
298 
299  double maxDistance = getThreshold(m_parameters.m_acceptanceThreshold, inputRasterBands.size());
300  while (!stable)
301  {
302  stable = true;
303 
304  compared.clear();
305 
306 // analyze all regions
307  for (rit = m_regions.begin(); rit != m_regions.end(); ++rit)
308  {
309 // compare all clusters to detect if they are still similar
310  for (riti = rit; riti != m_regions.end(); ++riti)
311  {
312  if (compared.find(std::pair<unsigned int, unsigned int>(rit->second->m_myCluster->m_id, riti->second->m_myCluster->m_id)) == compared.end())
313  {
314  compared.insert(std::pair<unsigned int, unsigned int>(rit->second->m_myCluster->m_id, riti->second->m_myCluster->m_id));
315  compared.insert(std::pair<unsigned int, unsigned int>(riti->second->m_myCluster->m_id, rit->second->m_myCluster->m_id));
316  }
317  else
318  continue;
319 
320  if (rit->second->m_myCluster == riti->second->m_myCluster)
321  continue;
322 
323 // use the smallest distance between region1 <-> cluster2, and region2 <-> cluster1
324  distance = rit->second->m_myCluster->getDistance(riti->second->m_myCluster);
325 
326  distance2 = riti->second->m_myCluster->getDistance(rit->second->m_myCluster);
327 
328  distance = (distance2 < distance? distance2: distance);
329 
330  if (distance <= maxDistance)
331  {
332 // two clusters are similar and must be merged
333  stable = false;
334 
335  oldid = riti->second->m_myCluster->m_id;
336 
337  for (ritii = riti; ritii != m_regions.end(); ++ritii)
338  {
339 // find all clusters with riti class and merge with rit cluster
340  if (ritii->second->m_myCluster->m_id != oldid)
341  continue;
342 
343  ritii->second->m_myCluster = rit->second->m_myCluster;
344  }
345  }
346  }
347  }
348  }
349 
350 // remap cluster values to 1 -> N
351  std::set<unsigned int> ulabels;
352  std::set<unsigned int>::iterator lit;
353  std::map<unsigned int, unsigned int> colormap;
354 
355  for (rit = m_regions.begin(); rit != m_regions.end(); ++rit)
356  ulabels.insert(rit->second->m_myCluster->m_id);
357 
358  unsigned int color = 1;
359  for (lit = ulabels.begin(); lit != ulabels.end(); ++lit)
360  colormap[*lit] = color++;
361 
362  te::gm::Coord2D geoCoord;
363  unsigned int pattern;
364 
365 // classify output image
366  te::common::TaskProgress task(TR_RP("ISOSeg algorithm - classifying image"), te::common::TaskProgress::UNDEFINED, m_regions.size());
367  for (rit = m_regions.begin(); rit != m_regions.end(); ++rit)
368  {
369  te::gm::Polygon* polygon = inputPolygons[rit->second->m_id];
370 
371  te::gm::Coord2D ll = polygon->getMBR()->getLowerLeft();
372  te::gm::Coord2D ur = polygon->getMBR()->getUpperRight();
373 
374  te::gm::Coord2D startGridCoord = outputRaster.getGrid()->geoToGrid(ll.x, ur.y);
375  te::gm::Coord2D endGridCoord = outputRaster.getGrid()->geoToGrid(ur.x, ll.y);
376 
377  double tmpCoord;
378  if (startGridCoord.y > endGridCoord.y)
379  {
380  tmpCoord = startGridCoord.y;
381  startGridCoord.y = endGridCoord.y;
382  endGridCoord.y = tmpCoord;
383  }
384 
385  pattern = rit->second->m_myCluster->m_id;
386 
387 // iterate over polygon to classify output image
390 
391  while (it != itend)
392  {
393  outputRaster.setValue(it.getColumn(), it.getRow(), colormap[pattern], outputRasterBand);
394 
395  ++it;
396  }
397  task.pulse();
398  }
399 
400  return true;
401 }
402 
404  : te::rp::ClassifierStrategyFactory("isoseg")
405 {
406 }
407 
409 {
410 }
411 
413 {
415 }
double m_acceptanceThreshold
The acceptance threshold (the closer to 100%, few clusters are created).
double m_area
The area of all regions inside a pattern.
AbstractParameters * clone() const
Create a clone copy of this instance.
Polygon is a subclass of CurvePolygon whose rings are defined by linear rings.
Definition: Polygon.h:50
Raster strategy parameters base class.
double getArea() const
It returns the area of this surface, as measured in the spatial reference system of this surface...
unsigned int getRow() const
Returns the current row in iterator.
Raster Processing functions.
te::rp::ClassifierStrategy * build()
Concrete factories (derived from this one) must implement this method in order to create objects...
Describes a region or a cluster (group of regions with similar properties) to be used by ISOSeg metho...
double y
y-coordinate.
Definition: Coord2D.h:87
boost::numeric::ublas::matrix< double > m_covarianceMatrix
The covariance matrix between bands.
double getThreshold(double acceptanceThreshold, unsigned nBands)
void reset()
Clear all internal allocated resources and reset the parameters instance to its initial state...
#define TR_RP(message)
It marks a string in order to get translated. This is a special mark used in the Raster Processing mo...
Definition: Config.h:58
std::vector< std::complex< double > > m_meanVector
The vector of mean values, 1 value per band;.
An utility struct for representing 2D coordinates.
Definition: Coord2D.h:40
static PolygonIterator begin(const te::rst::Raster *r, const te::gm::Polygon *p)
Returns an iterator referring to the first value of the band.
Coord2D getUpperRight() const
It returns the upper right coordinate of the envelope.
Definition: Envelope.cpp:43
#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
double getDistance(Pattern *p)
Returns the Mahalanobis distance between two patterns.
ISOSeg strategy for segmentation-based classification.
Abstract parameters base interface.
Defines the exact sequence of characters that are acceptable.
Definition: Enums.h:102
Pattern * m_myCluster
The associated cluster of this pattern (optional).
const Parameters & operator=(const Parameters &params)
boost::numeric::ublas::matrix< double > m_covarianceInversion
The inversion of covariance matrix between bands.
Raster classifier strategy factory base class.
Extraction of attributes from Raster, Bands, and Polygons.
This class implements the strategy to iterate with spatial restriction, the iteration occurs inside a...
std::multimap< double, Pattern *, std::greater< double > > m_regions
A descriptive set of regions (area, features).
bool m_isInitialized
True if this instance is initialized.
Extraction of attributes from Raster, Bands, and Polygons.
bool operator=(Pattern &rhs)
Return true if two clusters are equal.
void add(Pattern *p)
Add a region inside a cluster.
bool getInverseMatrix(const boost::numeric::ublas::matrix< T > &inputMatrix, boost::numeric::ublas::matrix< T > &outputMatrix)
Matrix inversion.
Definition: MatrixUtils.h:104
unsigned int getColumn() const
Returns the current column in iterator.
int m_id
The id of the region of the pattern.
ClassifierISOSegStrategy::Parameters m_parameters
Internal execution parameters.
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.
An abstract class for raster data strucutures.
Definition: Raster.h:70
Raster ISOSeg Classifier strategy factory.
This class can be used to inform the progress of a task.
Definition: TaskProgress.h:53
const Envelope * getMBR() const
It returns the minimum bounding rectangle for the geometry in an internal representation.
Definition: Geometry.cpp:103
static PolygonIterator end(const te::rst::Raster *r, const te::gm::Polygon *p)
Returns an iterator referring to after the end of the iterator.
double x
x-coordinate.
Definition: Coord2D.h:86
Raster classifier strategy base class.
Coord2D getLowerLeft() const
It returns the lower left coordinate of the envelope.
Definition: Envelope.cpp:38
bool initialize(StrategyParameters const *const strategyParams)
Initialize the classification strategy.
ISOSeg strategy for OBIA classification. The algorithm orders regions by area (larger first)...