All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
MixtureModelPCAStrategy.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/MixtureModelPCAStrategy.cpp
22 
23  \brief PCA (Principal Component Analysis) strategy for mixture model.
24 */
25 
26 // TerraLib
27 #include "../common/MatrixUtils.h"
28 #include "../common/progress/TaskProgress.h"
29 #include "../raster/Grid.h"
30 #include "../raster/Utils.h"
31 #include "Functions.h"
32 #include "Macros.h"
34 #include "Functions.h"
35 
36 // Boost
37 #include <boost/numeric/ublas/io.hpp>
38 #include <boost/numeric/ublas/matrix.hpp>
39 
40 bool gaussElimination(boost::numeric::ublas::matrix<double>& Z, std::vector<int>& row, unsigned int nComponents);
41 bool fowardBackSubstitution(boost::numeric::ublas::matrix<double>& Z, std::vector<double>& y, unsigned int ys, std::vector<int>& row, unsigned int rows, std::vector<double>& x, unsigned int xs);
42 
43 namespace
44 {
45  static te::rp::MixtureModelPCAStrategyFactory mixtureModelPCAStrategyFactoryInstance;
46 }
47 
49 {
50  reset();
51 }
52 
54 {
55 }
56 
58 {
59  reset();
60 
61  return *this;
62 }
63 
65 {
66 }
67 
69 {
71 }
72 
74 {
75  m_isInitialized = false;
76 }
77 
79 {
80 }
81 
82 bool te::rp::MixtureModelPCAStrategy::initialize(te::rp::StrategyParameters const* const strategyParams) throw(te::rp::Exception)
83 {
84  m_isInitialized = false;
85 
86  te::rp::MixtureModelPCAStrategy::Parameters const* paramsPtr = dynamic_cast<te::rp::MixtureModelPCAStrategy::Parameters const*>(strategyParams);
87 
88  if(!paramsPtr)
89  return false;
90 
91  m_parameters = *(paramsPtr);
92 
93  // TERP_TRUE_OR_RETURN_FALSE(m_parameters.m_acceptanceThreshold > 0 && m_parameters.m_acceptanceThreshold <= 100, "Invalid acceptance threshold [0, 100].")
94 
95  m_isInitialized = true;
96 
97  return true;
98 }
99 
100 bool te::rp::MixtureModelPCAStrategy::execute(const te::rst::Raster& inputRaster, const std::vector<unsigned int>& inputRasterBands,
101  const std::vector<std::string>& inputSensorBands, const std::map<std::string, std::vector<double> >& components,
102  te::rst::Raster& outputRaster, const bool enableProgressInterface) throw(te::rp::Exception)
103 {
104 // this source code is based on TerraLib 4
105  TERP_TRUE_OR_RETURN_FALSE(m_isInitialized, "Instance not initialized")
106 
107  const unsigned int nComponents = components.size();
108  const unsigned int nBands = inputSensorBands.size();
109 
110  bool computeErrorRasters = false;
111  if (nComponents < outputRaster.getNumberOfBands())
112  computeErrorRasters = true;
113 
114 // indexes variables
115  unsigned int i, j, k, m;
116 
117  std::vector<double> Qmax;
118  for (i = 0; i < inputSensorBands.size(); i++)
119  Qmax.push_back(GetDigitalNumberBandMax(inputSensorBands[i]));
120 
121 // buildind the SpectralBandsComponents Matrix
122  boost::numeric::ublas::matrix<double> SpectralBandsComponents(nBands, nComponents);
123  std::map<std::string, std::vector<double> >::const_iterator it;
124  for(i = 0; i < nBands; i++)
125  for (j = 0, it = components.begin(); it != components.end(); j++, it++)
126  SpectralBandsComponents(i, j) = it->second[i] / Qmax[i];
127 
128 // Part I : mathematical processing independend on image data
129 
130 // SpectralBandsComponentsTransposed stores SpectralBandsComponents and other things after
131  boost::numeric::ublas::matrix<double> SpectralBandsComponentsTransposed(nComponents, nBands);
132 
133 // Initializing SpectralBandsComponentsTransposed Matrix = Transpose of SpectralBandsComponents
134  SpectralBandsComponentsTransposed = boost::numeric::ublas::trans(SpectralBandsComponents);
135 
136 // compute componentsMean vector as the mean coefficient value per band and subtract the componentsMeanAfter from the coefficients matrix
137 // creating two double vectors to store de means of the spectralBands of each component
138  std::vector<double> componentsMean(nBands, 0.0);
139  std::vector<double> componentsMeanAfter(nBands, 0.0);
140 
141  for (j = 0; j < nBands; j++)
142  {
143 // compute original mean (componentsMean)
144  componentsMean[j] = 0;
145  for(i = 0; i < nComponents; i++)
146  componentsMean[j] = componentsMean[j] + SpectralBandsComponentsTransposed(i, j);
147  componentsMean[j] = componentsMean[j] / (double) nComponents;
148 
149 // subtract mean from SpectralBandsComponents and compute new matrix mean (componentsMeanAfter)
150  componentsMeanAfter[j] = 0;
151  for(i = 0; i < nComponents; i++)
152  {
153  SpectralBandsComponentsTransposed(i, j) = SpectralBandsComponentsTransposed(i, j) - componentsMean[j];
154  componentsMeanAfter[j] = componentsMeanAfter[j] + SpectralBandsComponentsTransposed(i, j);
155  }
156  componentsMeanAfter[j] = componentsMeanAfter[j] / (double) nComponents;
157  }
158 
159 // compute covarianceVector vector
160  std::vector<double> covarianceVector(nBands * nBands, 0.0);
161  for(k = 0; k < nBands * nBands; k++)
162  covarianceVector[k] = 0;
163 
164  k = 0 ;
165  for(j = 0; j < nBands; j++)
166  for(m = 0; m < j + 1; m++)
167  {
168  for(i = 0; i < nComponents; i++)
169  covarianceVector[k] = covarianceVector[k] + (SpectralBandsComponentsTransposed(i, j) - componentsMeanAfter[j]) *
170  (SpectralBandsComponentsTransposed(i, m) - componentsMeanAfter[m]);
171  covarianceVector[k] = covarianceVector[k] / (double) nComponents;
172 
173  k++;
174  }
175 
176  k = 0;
177  boost::numeric::ublas::matrix<double> covarianceMatrix(nBands, nBands);
178  for (i = 0; i < nBands; i++)
179  for (j = 0; j <= i; j++)
180  covarianceMatrix(i, j) = covarianceVector[k++];
181 
182 // compute eigenvectors -> auxMatrix and eigenvalues -> tmpMatrix
183  boost::numeric::ublas::matrix<double> auxMatrix(covarianceMatrix);
184  boost::numeric::ublas::matrix<double> tmpMatrix;
185 
186  if(!te::common::EigenVectors(covarianceMatrix, auxMatrix, tmpMatrix))
187  return false; // print error in eigenvectors
188 
189 // keep only significant eigenvectors (nComponents - 1): results eigenReduced
190  boost::numeric::ublas::matrix<double> eigenReduced(nBands, nComponents - 1);
191  for (j = 0; j < nBands; j++)
192  for (i = 0; i < nComponents - 1; i++)
193  eigenReduced(j, i) = auxMatrix(j, i);
194 
195 // rotate SpectralBandsComponentsTransposed to PCA space; result SpectralBandsComponentsPCA
196  boost::numeric::ublas::matrix<double> SpectralBandsComponentsPCA(SpectralBandsComponentsTransposed.size1(), eigenReduced.size2()); // (nrow x ncol)???
197  SpectralBandsComponentsPCA = boost::numeric::ublas::prod(SpectralBandsComponentsTransposed, eigenReduced);
198 
199 // Initialize one more column to SpectralBandsComponentsPCA to incorporate sum restriction to equations; results auxMatrix
200  auxMatrix.resize(SpectralBandsComponentsPCA.size1(), SpectralBandsComponentsPCA.size2() + 1); // nrow, ncol + 1 ???
201  auxMatrix.clear();
202 
203  for (j = 0; j < SpectralBandsComponentsPCA.size1(); j++) // nrow
204  {
205  for (i = 0; i < SpectralBandsComponentsPCA.size2(); i++) // ncol
206  auxMatrix(j, i) = SpectralBandsComponentsPCA(j, i);
207 
208  auxMatrix(j, SpectralBandsComponentsPCA.size2()) = 1.0;
209  }
210 
211 // Transpose auxMatrix; results SpectralBandsComponentsPCA
212  SpectralBandsComponentsPCA.clear();
213  SpectralBandsComponentsPCA = boost::numeric::ublas::trans(auxMatrix);
214 
215 // invert matrix SpectralBandsComponentsPCA; results SpectralBandsComponentsPCA
216  std::vector<int> rows(nComponents, 0);
217 
218  if (!gaussElimination(SpectralBandsComponentsPCA, rows, nComponents))
219  return false; // print "Error in gauss elimination");
220 
221 // Part II : mathematical processing dependend on image data
222 
223 // Initialize matrices that will help to prepare vector Y
224  auxMatrix.resize(1, nBands);
225  auxMatrix.clear();
226  boost::numeric::ublas::matrix<double> ymat(nComponents - 1, nComponents - 1);
227 
228 // initialize proportion vector X and image dependent values for the linear equations Y
229  std::vector<double> x(nComponents, 0.0);
230  std::vector<double> y(nComponents, 0.0);
231 
232  unsigned int prop;
233  std::vector<unsigned int> rowsIn(nBands, 0);
234  std::vector<unsigned int> columnIn(nBands, 0);
235  std::vector<unsigned int> columnsIn(nBands, 0);
236  std::vector<double> spectralBandsError(nBands, 0.0);
237 
238  boost::numeric::ublas::matrix<double> rasterRowsIn(nBands, inputRaster.getNumberOfColumns());
239  boost::numeric::ublas::matrix<double> rasterRowsOut(nComponents, inputRaster.getNumberOfColumns());
240 
241  te::common::TaskProgress task(TE_TR("PCA Mixture Model"), te::common::TaskProgress::UNDEFINED, inputRaster.getNumberOfRows());
242  double value;
243  for (unsigned int rowout = 0; rowout < inputRaster.getNumberOfRows(); rowout++)
244  {
245 // read input row for each band
246  for (i = 0; i < nBands; i++)
247  {
248  for (j = 0; j < inputRaster.getNumberOfColumns(); j++)
249  {
250  inputRaster.getValue(j, rowsIn[i], value, inputRasterBands[i]);
251  rasterRowsIn(i, j) = value;
252  }
253 
254 // update next row to be read for band i
255  rowsIn[i]++;
256 
257 // reinitialize first column for band i
258  columnIn[i] = columnsIn[i];
259  }
260 
261 // compute proportions for each column
262  for (unsigned int colout = 0; colout < inputRaster.getNumberOfColumns(); colout++)
263  {
264 // prepare y
265  for (i = 0; i < nBands; i++)
266  auxMatrix(0, i) = (double) (rasterRowsIn(i, columnIn[i])) / 255.0 - componentsMean[i];
267 
268  ymat = boost::numeric::ublas::prod(auxMatrix, eigenReduced);
269 
270  for (i = 0; i < nComponents - 1; i++)
271  y[i] = ymat(0, i);
272 
273 // compute proportions
274  if (!fowardBackSubstitution(SpectralBandsComponentsPCA, y, nComponents, rows, nComponents, x, nComponents))
275  return false; // print "error in function fowardBackSubstitution"
276 
277 // store proportion in buffers
278  for (i = 0; i < nComponents; i++)
279  {
280  prop = (short) (x[i] * 100 + 100);
281  rasterRowsOut(i, colout) = (unsigned char) prop;
282  }
283 
284  double aux;
285  double error;
286 
287 // verify if it is necessary to compute the error
288  if (computeErrorRasters)
289  {
290  for (i = 0; i < nBands; i++)
291  {
292 // compute digital value from the proportions
293  aux = 0.0;
294  for (j = 0; j < nComponents; j++)
295  aux += x[j] * SpectralBandsComponents(i, j);
296 
297 // compute error as module of the difference between the original value and aux
298  error = (long) (rasterRowsIn(i, columnIn[i])) / 255.0 - aux;
299  if (error < 0)
300  error *= -1;
301 
302  if (computeErrorRasters)
303  rasterRowsIn(i, colout) = error * 255.0;
304  }
305  }
306 
307 // update current column number
308  for (i = 0; i < nBands; i++)
309  columnIn[i]++;
310 
311  }
312 
313 // write processed row to disk
314  for (i = 0; i < nComponents; i++)
315  for (j = 0; j < inputRaster.getNumberOfColumns(); j++)
316  outputRaster.setValue(j, rowout, rasterRowsOut(i, j), i);
317 
318 // store output error rasters
319  if (computeErrorRasters)
320  {
321  for (i = 0; i < nComponents; i++)
322  for (j = 0; j < inputRaster.getNumberOfColumns(); j++)
323  outputRaster.setValue(j, rowout, rasterRowsIn(i, j), i + nComponents); // ???
324  }
325  task.pulse();
326  }
327 
328  return true;
329 }
330 
332  : te::rp::MixtureModelStrategyFactory("pca")
333 {
334 }
335 
337 {
338 }
339 
341 {
342  return new te::rp::MixtureModelPCAStrategy();
343 }
344 
345 bool gaussElimination(boost::numeric::ublas::matrix<double>& Z, std::vector<int>& row, unsigned int nComponents)
346 {
347 // verify matrix and vector sizes
348  assert(Z.size1() >= Z.size2());
349  unsigned int nrows = Z.size1(); // rows = size2?
350  assert(nComponents >= nrows);
351 
352  unsigned int i;
353  unsigned int j;
354  unsigned int k;
355  unsigned int aux;
356  double auxd;
357  double m;
358 
359 // initialize changed rows indicator
360  for (i = 0; i < nrows; i++)
361  row[i] = i;
362 
363 // diagonalization
364  for (k = 0; k < nrows; k++)
365  {
366  i = k;
367  while (Z(i, k) == 0.0)
368  {
369  if (i == nrows - 1)
370  return false; // print "Singular matrix"
371 
372  i++;
373  }
374 
375  if (k != i)
376  {
377 // update changed rows indicator
378  aux = row[i];
379  row[i] = row[k];
380  row[k] = aux;
381 
382 // change rows
383  for (j=0; j < nrows; j ++)
384  {
385  auxd = Z(k, j);
386  Z(k, j) = Z (i, j);
387  Z (i, j) = auxd;
388  }
389  }
390 
391 // recompute rows i = k + 1,..., nComponents - 1
392  for (i = k + 1; i < nrows; i++)
393  {
394  m = Z(i, k)/Z(k, k);
395  Z(i, k) = m;
396  for (j = k + 1; j < nrows; j++)
397  Z (i, j) = Z (i, j) - m * Z(k, j);
398  }
399 
400  }
401 
402  if (Z(nrows - 1, nrows - 1) == 0.0)
403  return false; // print "Singular Matrix"
404 
405  return true;
406 }
407 
408 bool fowardBackSubstitution(boost::numeric::ublas::matrix<double>& Z, std::vector<double>& y, unsigned int ys, std::vector<int>& row, unsigned int rows, std::vector<double>& x, unsigned int xs)
409 {
410 // verify matrix size
411  assert(Z.size1() >= Z.size2());
412  unsigned int nrows = Z.size1(); // rows = size2?
413 
414  if (((rows < nrows) || (ys < nrows) || (xs < nrows)))
415  return false; // print "Vector Size"
416 
417  int j, k;
418  double aux;
419  std::vector<double> F(nrows, 0.0);
420 
421 // foward substuitution
422  for (k = 0; k < (int) nrows; k++)
423  {
424  aux = 0.0;
425  for (j = 0; j <= k - 1; j++)
426  aux = aux + Z(k, j) * F[j];
427 
428  F[k] = y[row[k]] - aux;
429  }
430 
431 // backward substitution
432  for (k = ((int) nrows) - 1; k >= 0; k--)
433  {
434  aux = 0.0;
435  for (j = k + 1; j < (int) nrows; j++)
436  aux = aux + Z(k, j) * x[j];
437 
438  x[k] = (F[k] - aux) / Z(k, k);
439  }
440 
441  return true;
442 }
double GetDigitalNumberBandMax(std::string bandName)
Returns the maximum digital number of a given sensor/band.
Definition: Functions.cpp:696
Raster Mixture model strategy factory base class.
bool fowardBackSubstitution(boost::numeric::ublas::matrix< double > &Z, std::vector< double > &y, unsigned int ys, std::vector< int > &row, unsigned int rows, std::vector< double > &x, unsigned int xs)
bool initialize(StrategyParameters const *const strategyParams)
Initialize the segmentation strategy.
This class can be used to inform the progress of a task.
Definition: TaskProgress.h:53
void reset()
Clear all internal allocated resources and reset the parameters instance to its initial state...
#define TE_TR(message)
It marks a string in order to get translated.
Definition: Translator.h:347
MixtureModelPCAStrategy::Parameters m_parameters
Internal execution parameters.
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 mixture model strategy base class.
An abstract class for raster data strucutures.
Definition: Raster.h:71
const Parameters & operator=(const Parameters &params)
Raster strategy parameters base class.
Raster PCA mixture model strategy factory.
bool EigenVectors(const boost::numeric::ublas::matrix< T > &inputMatrix, boost::numeric::ublas::matrix< T > &eigenVectorsMatrix, boost::numeric::ublas::matrix< T > &eigenValuesMatrix)
Computes the eigenvectors of a given matrix.
Definition: MatrixUtils.h:211
Abstract parameters base interface.
bool gaussElimination(boost::numeric::ublas::matrix< double > &Z, std::vector< int > &row, unsigned int nComponents)
AbstractParameters * clone() const
Create a clone copy of this instance.
bool m_isInitialized
True if this instance is initialized.
bool execute(const te::rst::Raster &inputRaster, const std::vector< unsigned int > &inputRasterBands, const std::vector< std::string > &inputSensorBands, const std::map< std::string, std::vector< double > > &components, te::rst::Raster &outputRaster, const bool enableProgressInterface)
Executes the segmentation strategy.
PCA (Principal Component Analysis) strategy for mixture model.
te::rp::MixtureModelStrategy * build()
Concrete factories (derived from this one) must implement this method in order to create objects...