OTB  9.0.0
Orfeo Toolbox
otbPCAImageFilter.hxx
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2005-2022 Centre National d'Etudes Spatiales (CNES)
3  *
4  * This file is part of Orfeo Toolbox
5  *
6  * https://www.orfeo-toolbox.org/
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 #ifndef otbPCAImageFilter_hxx
22 #define otbPCAImageFilter_hxx
23 #include "otbPCAImageFilter.h"
24 
25 #include "itkMacro.h"
26 
27 #include <vnl/vnl_matrix.h>
28 #include <vnl/algo/vnl_symmetric_eigensystem.h>
29 #include <vnl/algo/vnl_generalized_eigensystem.h>
30 
31 namespace otb
32 {
33 
34 template <class TInputImage, class TOutputImage, Transform::TransformDirection TDirectionOfTransformation>
36 {
37  this->SetNumberOfRequiredInputs(1);
38 
39  m_NumberOfPrincipalComponentsRequired = 0;
40  m_Whitening = true;
41  m_UseNormalization = false;
42  m_UseVarianceForNormalization = false;
43  m_GivenMeanValues = false;
44  m_GivenStdDevValues = false;
45 
46  m_GivenCovarianceMatrix = false;
47  m_GivenTransformationMatrix = false;
48  m_IsTransformationMatrixForward = true;
49 
50  m_CovarianceEstimator = CovarianceEstimatorFilterType::New();
51  m_Transformer = TransformFilterType::New();
52  m_Transformer->MatrixByVectorOn();
53  m_Normalizer = NormalizeFilterType::New();
54 }
55 
56 template <class TInputImage, class TOutputImage, Transform::TransformDirection TDirectionOfTransformation>
58 {
59  Superclass::GenerateOutputInformation();
60 
61  switch (static_cast<int>(DirectionOfTransformation))
62  {
63  case static_cast<int>(Transform::FORWARD):
64  {
65  if (m_NumberOfPrincipalComponentsRequired == 0 || m_NumberOfPrincipalComponentsRequired > this->GetInput()->GetNumberOfComponentsPerPixel())
66  {
67  m_NumberOfPrincipalComponentsRequired = this->GetInput()->GetNumberOfComponentsPerPixel();
68  }
69 
70  this->GetOutput()->SetNumberOfComponentsPerPixel(m_NumberOfPrincipalComponentsRequired);
71  break;
72  }
73  case static_cast<int>(Transform::INVERSE):
74  {
75  unsigned int theOutputDimension = 0;
76  if (m_GivenTransformationMatrix)
77  {
78  theOutputDimension = m_TransformationMatrix.Rows() >= m_TransformationMatrix.Cols() ? m_TransformationMatrix.Rows() : m_TransformationMatrix.Cols();
79  }
80  else if (m_GivenCovarianceMatrix)
81  {
82  theOutputDimension = m_CovarianceMatrix.Rows() >= m_CovarianceMatrix.Cols() ? m_CovarianceMatrix.Rows() : m_CovarianceMatrix.Cols();
83  }
84  else
85  {
86  throw itk::ExceptionObject(__FILE__, __LINE__, "Covariance or transformation matrix required to know the output size", ITK_LOCATION);
87  }
88 
89  this->GetOutput()->SetNumberOfComponentsPerPixel(theOutputDimension);
90 
91  break;
92  }
93  default:
94  throw itk::ExceptionObject(__FILE__, __LINE__, "Class should be templeted with FORWARD or INVERSE only...", ITK_LOCATION);
95  }
96 
97 
98  switch (static_cast<int>(DirectionOfTransformation))
99  {
100  case static_cast<int>(Transform::FORWARD):
101  {
102  ForwardGenerateOutputInformation();
103  break;
104  }
105  case static_cast<int>(Transform::INVERSE):
106  {
107  ReverseGenerateOutputInformation();
108  break;
109  }
110  }
111 }
112 
113 template <class TInputImage, class TOutputImage, Transform::TransformDirection TDirectionOfTransformation>
115 {
116  typename InputImageType::Pointer inputImgPtr = const_cast<InputImageType*>(this->GetInput());
117 
118  if (!m_GivenTransformationMatrix)
119  {
120  if (!m_GivenCovarianceMatrix)
121  {
122  if (m_UseNormalization)
123  {
124  m_Normalizer->SetInput(inputImgPtr);
125  m_Normalizer->SetUseStdDev(m_UseVarianceForNormalization);
126 
127  if (m_GivenMeanValues)
128  m_Normalizer->SetMean(m_MeanValues);
129 
130  if (m_GivenStdDevValues)
131  m_Normalizer->SetStdDev(m_StdDevValues);
132 
133  m_Normalizer->GetOutput()->UpdateOutputInformation();
134 
135  if (!m_GivenMeanValues)
136  {
137  m_MeanValues = m_Normalizer->GetCovarianceEstimator()->GetMean();
138  // Set User mean value so the filter won't recompute the stats
139  m_Normalizer->SetMean(m_MeanValues);
140 
141  if (!m_GivenStdDevValues)
142  {
143  m_StdDevValues = m_Normalizer->GetFunctor().GetStdDev();
144  }
145  if (m_UseVarianceForNormalization)
146  {
147  // Set User std value so the filter won't recompute the stats
148  m_Normalizer->SetStdDev(m_StdDevValues);
149 
150  // Compute the correlation matrix, note that GetCovarianceEstimator()->GetCorrelation()
151  // would give us the matrix with component E[XY], which is not what we want., we want
152  // the matrix defined by its component (E[XY]-E[X]E[Y])/(sigmaX*sigmaY)
153 
154  m_CovarianceMatrix = m_Normalizer->GetCovarianceEstimator()->GetCovariance();
155 
156  auto cov = m_Normalizer->GetCovarianceEstimator()->GetCovariance();
157  auto numberOfComponent = this->GetInput()->GetNumberOfComponentsPerPixel();
158 
159  for (unsigned int r = 0; r < numberOfComponent; ++r)
160  {
161  for (unsigned int c = 0; c < numberOfComponent; ++c)
162  {
163  m_CovarianceMatrix(r, c) = cov(r, c) / std::sqrt(cov(r, r) * cov(c, c));
164  }
165  }
166  }
167  else
168  {
169  m_Normalizer->SetUseStdDev(false);
170  m_CovarianceMatrix = m_Normalizer->GetCovarianceEstimator()->GetCovariance();
171  }
172  }
173  else
174  {
175  m_CovarianceEstimator->SetInput(m_Normalizer->GetOutput());
176  m_CovarianceEstimator->UpdateOutputInformation();
177  m_CovarianceMatrix = m_CovarianceEstimator->GetCovariance();
178  }
179 
180  m_Transformer->SetInput(m_Normalizer->GetOutput());
181  }
182  else
183  {
184  m_CovarianceEstimator->SetInput(inputImgPtr);
185  m_CovarianceEstimator->Update();
186 
187  m_CovarianceMatrix = m_CovarianceEstimator->GetCovariance();
188 
189  m_Transformer->SetInput(inputImgPtr);
190  }
191  }
192  else
193  {
194  m_Transformer->SetInput(inputImgPtr);
195  }
196 
197  GenerateTransformationMatrix();
198  }
199  else if (!m_IsTransformationMatrixForward)
200  {
201  // m_TransformationMatrix = m_TransformationMatrix.GetTranspose();
202  m_TransformationMatrix = m_TransformationMatrix.GetInverse();
203 
204  m_Transformer->SetInput(inputImgPtr);
205  }
206 
207  if (m_TransformationMatrix.GetVnlMatrix().empty())
208  {
209  throw itk::ExceptionObject(__FILE__, __LINE__, "Empty transformation matrix", ITK_LOCATION);
210  }
211 }
212 
213 template <class TInputImage, class TOutputImage, Transform::TransformDirection TDirectionOfTransformation>
215 {
216  if (!m_GivenTransformationMatrix)
217  {
218  if (!m_GivenCovarianceMatrix)
219  {
220  throw itk::ExceptionObject(__FILE__, __LINE__, "No Covariance nor Transformation matrix given", ITK_LOCATION);
221  }
222 
223  GenerateTransformationMatrix();
224  // m_TransformationMatrix = m_TransformationMatrix.GetTranspose();
225  m_TransformationMatrix = m_TransformationMatrix.GetInverse();
226  }
227  else if (m_IsTransformationMatrixForward)
228  {
229  // prevents from multiple transpositions...
230  m_IsTransformationMatrixForward = false;
231  // m_TransformationMatrix = m_TransformationMatrix.GetTranspose();
232  m_TransformationMatrix = m_TransformationMatrix.GetInverse();
233  }
234 
235  if (m_TransformationMatrix.GetVnlMatrix().empty())
236  {
237  throw itk::ExceptionObject(__FILE__, __LINE__, "Empty transformation matrix", ITK_LOCATION);
238  }
239 
240  m_Transformer->SetInput(this->GetInput());
241  m_Transformer->SetMatrix(m_TransformationMatrix.GetVnlMatrix());
242  m_Normalizer->SetInput(m_Transformer->GetOutput());
243 
244  if (m_GivenStdDevValues || m_GivenMeanValues)
245  {
246  if (m_GivenStdDevValues)
247  {
248  VectorType revStdDev(m_StdDevValues.Size());
249  for (unsigned int i = 0; i < m_StdDevValues.Size(); ++i)
250  revStdDev[i] = 1. / m_StdDevValues[i];
251  m_Normalizer->SetStdDev(revStdDev);
252  }
253 
254  if (m_GivenMeanValues)
255  {
256  VectorType revMean(m_MeanValues.Size());
257  if (m_GivenStdDevValues)
258  {
259  for (unsigned int i = 0; i < m_MeanValues.Size(); ++i)
260  revMean[i] = -m_MeanValues[i] / m_StdDevValues[i];
261  m_Normalizer->SetUseStdDev(true);
262  }
263  else
264  {
265  for (unsigned int i = 0; i < m_MeanValues.Size(); ++i)
266  revMean[i] = -m_MeanValues[i];
267  m_Normalizer->SetUseStdDev(false);
268  }
269  m_Normalizer->SetMean(revMean);
270  }
271  }
272  else
273  {
274  m_Normalizer->SetUseMean(false);
275  m_Normalizer->SetUseStdDev(false);
276  }
277 }
278 
279 
280 template <class TInputImage, class TOutputImage, Transform::TransformDirection TDirectionOfTransformation>
282 {
283  switch (static_cast<int>(DirectionOfTransformation))
284  {
285  case static_cast<int>(Transform::FORWARD):
286  return ForwardGenerateData();
287  case static_cast<int>(Transform::INVERSE):
288  return ReverseGenerateData();
289  default:
290  throw itk::ExceptionObject(__FILE__, __LINE__, "Class should be templated with FORWARD or INVERSE only...", ITK_LOCATION);
291  }
292 }
293 
294 template <class TInputImage, class TOutputImage, Transform::TransformDirection TDirectionOfTransformation>
296 {
297  m_Transformer->SetMatrix(m_TransformationMatrix.GetVnlMatrix());
298  m_Transformer->GraftOutput(this->GetOutput());
299  m_Transformer->Update();
300  this->GraftOutput(m_Transformer->GetOutput());
301 }
302 
303 template <class TInputImage, class TOutputImage, Transform::TransformDirection TDirectionOfTransformation>
305 {
306 
307  if (m_GivenStdDevValues || m_GivenMeanValues)
308  {
309  m_Normalizer->GraftOutput(this->GetOutput());
310  m_Normalizer->Update();
311  this->GraftOutput(m_Normalizer->GetOutput());
312  }
313  else
314  {
315  m_Transformer->GraftOutput(this->GetOutput());
316  m_Transformer->Update();
317  this->GraftOutput(m_Transformer->GetOutput());
318  }
319 }
320 
321 template <class TInputImage, class TOutputImage, Transform::TransformDirection TDirectionOfTransformation>
323 {
324  InternalMatrixType transf;
325  vnl_vector<double> vectValP;
326  vnl_symmetric_eigensystem_compute(m_CovarianceMatrix.GetVnlMatrix(), transf, vectValP);
327 
328 
329  m_EigenValues.SetSize(m_NumberOfPrincipalComponentsRequired);
330  for (unsigned int i = 0; i < m_NumberOfPrincipalComponentsRequired; ++i)
331  m_EigenValues[m_NumberOfPrincipalComponentsRequired - 1 - i] = static_cast<RealType>(vectValP[i]);
332 
333  if (m_Whitening)
334  {
335  InternalMatrixType valP(vectValP.size(), vectValP.size(), vnl_matrix_null);
336  for (unsigned int i = 0; i < vectValP.size(); ++i)
337  valP(i, i) = vectValP[i];
338 
339  for (unsigned int i = 0; i < valP.rows(); ++i)
340  {
341  if (valP(i,i) != 0.0)
342  valP(i,i) = 1.0 / std::sqrt(std::abs(valP(i,i)));
343  else
344  throw itk::ExceptionObject(__FILE__, __LINE__, "Null Eigen value !!", ITK_LOCATION);
345  }
346  transf = valP * transf.transpose();
347  }
348  else {
349  transf = transf.transpose();
350  }
351 
352  transf.flipud();
353 
354  if (m_NumberOfPrincipalComponentsRequired != this->GetInput()->GetNumberOfComponentsPerPixel())
355  m_TransformationMatrix = transf.get_n_rows(0, m_NumberOfPrincipalComponentsRequired);
356  else
357  m_TransformationMatrix = transf;
358 }
359 
360 
361 template <class TInputImage, class TOutputImage, Transform::TransformDirection TDirectionOfTransformation>
363 {
364  Superclass::PrintSelf(os, indent);
365 
366  os << indent << "m_UseNormalization = ";
367  if (m_UseNormalization)
368  os << "true\n";
369  else
370  os << "false\n";
371 
372  if (m_GivenMeanValues)
373  os << indent << "Given Mean : " << m_MeanValues << "\n";
374 
375  if (m_GivenStdDevValues)
376  os << indent << "Given StdDev : " << m_StdDevValues << "\n";
377 
378  if (!m_CovarianceMatrix.GetVnlMatrix().empty())
379  {
380  os << indent << "Covariance matrix";
381  if (m_GivenCovarianceMatrix)
382  os << " (given)";
383  os << "\n";
384 
385  m_CovarianceMatrix.GetVnlMatrix().print(os);
386 
387  if (m_GivenCovarianceMatrix)
388  m_CovarianceEstimator->Print(os, indent.GetNextIndent());
389  }
390 
391  if (!m_TransformationMatrix.GetVnlMatrix().empty())
392  {
393  os << indent;
394  if (!m_IsTransformationMatrixForward)
395  os << "Invert ";
396  os << "Transformation matrix";
397  if (m_GivenTransformationMatrix)
398  os << " (given)";
399  os << "\n";
400 
401  m_TransformationMatrix.GetVnlMatrix().print(os);
402  }
403 
404  if (m_EigenValues.Size() > 0)
405  {
406  os << indent << "Eigen value :";
407  for (unsigned int i = 0; i < m_NumberOfPrincipalComponentsRequired; ++i)
408  os << " " << m_EigenValues[i];
409  os << "\n";
410  }
411 }
412 
413 } // end of namespace otb
414 
415 #endif // otbPCAImageFilter_hxx
otb::PCAImageFilter::ReverseGenerateOutputInformation
virtual void ReverseGenerateOutputInformation()
Definition: otbPCAImageFilter.hxx:214
otb::PCAImageFilter::InternalMatrixType
MatrixType::InternalMatrixType InternalMatrixType
Definition: otbPCAImageFilter.h:88
otb::PCAImageFilter::GenerateData
void GenerateData() override
Definition: otbPCAImageFilter.hxx:281
otb
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
Definition: otbJoinContainer.h:32
otb::Transform::FORWARD
@ FORWARD
Definition: otbPCAImageFilter.h:36
otb::PCAImageFilter::VectorType
CovarianceEstimatorFilterType::RealPixelType VectorType
Definition: otbPCAImageFilter.h:85
otb::Transform::INVERSE
@ INVERSE
Definition: otbPCAImageFilter.h:37
otb::PCAImageFilter::RealType
CovarianceEstimatorFilterType::RealType RealType
Definition: otbPCAImageFilter.h:84
otb::PCAImageFilter::PCAImageFilter
PCAImageFilter()
Definition: otbPCAImageFilter.hxx:35
otb::PCAImageFilter::InputImageType
TInputImage InputImageType
Definition: otbPCAImageFilter.h:77
otb::PCAImageFilter::GenerateTransformationMatrix
void GenerateTransformationMatrix()
Definition: otbPCAImageFilter.hxx:322
otbPCAImageFilter.h
otb::PCAImageFilter::ReverseGenerateData
virtual void ReverseGenerateData()
Definition: otbPCAImageFilter.hxx:304
otb::PCAImageFilter::ForwardGenerateData
virtual void ForwardGenerateData()
Definition: otbPCAImageFilter.hxx:295
otb::PCAImageFilter::GenerateOutputInformation
void GenerateOutputInformation() override
Definition: otbPCAImageFilter.hxx:57
otb::PCAImageFilter::PrintSelf
void PrintSelf(std::ostream &os, itk::Indent indent) const override
Definition: otbPCAImageFilter.hxx:362
otb::PCAImageFilter::ForwardGenerateOutputInformation
virtual void ForwardGenerateOutputInformation()
Definition: otbPCAImageFilter.hxx:114