OTB  9.0.0
Orfeo Toolbox
otbQuadraticallyConstrainedSimpleSolver.hxx
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1999-2011 Insight Software Consortium
3  * Copyright (C) 2005-2022 Centre National d'Etudes Spatiales (CNES)
4  * Copyright (C) 2016-2019 IRSTEA
5  *
6  * This file is part of Orfeo Toolbox
7  *
8  * https://www.orfeo-toolbox.org/
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  */
22 #ifndef QuadraticallyConstrainedSimpleSolver_hxx
23 #define QuadraticallyConstrainedSimpleSolver_hxx
24 
26 
27 namespace otb
28 {
29 
30 template <class ValueType>
32 {
33  m_WeightOfStandardDeviationTerm = 1.0;
34  oft = Cost_Function_rmse;
35 }
36 
37 template <class ValueType>
39 {
40 }
41 
42 /*
43  * Used to check layout topology consistency (Deep First Search)
44  *
45  * "m_AreaInOverlaps[i][j]>0" is equivalent to "images i and j are
46  * overlapping with a non empty intersection (i.e. non null data)"
47  */
48 template <class ValueType>
49 void QuadraticallyConstrainedSimpleSolver<ValueType>::DFS(std::vector<bool>& marked, unsigned int s) const
50 {
51 
52  // mark the s vertex
53  marked[s] = true;
54 
55  // Get neighborhood
56  for (unsigned int i = 0; i < m_AreaInOverlaps.rows(); i++)
57  {
58  if (s != i && m_AreaInOverlaps[s][i] > 0 && !marked[i])
59  {
60  DFS(marked, i);
61  }
62  }
63 }
64 
65 /*
66  * Check input matrices dimensions, and layout consistency
67  */
68 template <class ValueType>
70 {
71 
72  // Check area matrix is non empty
73  const unsigned int n = m_AreaInOverlaps.cols();
74  if (n == 0)
75  {
76  itkExceptionMacro(<< "Input area matrix has 0 elements");
77  }
78 
79  bool inputMatricesAreValid = true;
80 
81  // Check "areas" and "means" matrices size
82  if ((n != m_AreaInOverlaps.rows()) || (n != m_MeanInOverlaps.cols()) || (n != m_MeanInOverlaps.rows()))
83  {
84  inputMatricesAreValid = false;
85  }
86 
87  // Check "std" matrix size
88  if ((oft == Cost_Function_musig) || (oft == Cost_Function_weighted_musig) || (oft == Cost_Function_rmse))
89  {
90  if ((n != m_StandardDeviationInOverlaps.cols()) || (n != m_StandardDeviationInOverlaps.rows()))
91  {
92  inputMatricesAreValid = false;
93  }
94  }
95 
96  // Check "means of products" matrix size
97  if (oft == Cost_Function_musig)
98  {
99  if ((n != m_MeanOfProductsInOverlaps.cols()) || (n != m_MeanOfProductsInOverlaps.rows()))
100  {
101  inputMatricesAreValid = false;
102  }
103  }
104 
105  if (!inputMatricesAreValid)
106  {
107  itkExceptionMacro(<< "Input matrices must be square and have the same number of elements.");
108  }
109 }
110 
111 /*
112  * Compute the objective function
113  *
114  * VNL is not sufficient: it has weak solving routines, and can not deal with QP subject to
115  * a linear equality constraint plus lower limits (that is < and = linear constraints)
116  * With vnl, we keep it simple and solve only the zero-y intercept linear case
117  * but sometimes it fails because numerical instabilities. (vnl quadratic routines are not very reliable)
118  *
119  * With a good quadratic solver, we could e.g. use a general linear model (Xout = Xin*a+b)
120  * Unfortunately it can be done with VNL so far. Tested & implemented successfully with
121  * OOQP (Fastest, o(n)) and Quadprog++ (Fast, o(n)), and CGAL exact type solver (very slow, o(n^a) with a>1)
122  * but has to rely on external dependencies...
123  *
124  */
125 template <class ValueType>
128  const DoubleMatrixType& stds, const DoubleMatrixType& mops)
129 {
130  // Set STD matrix weight
131  ValueType w;
132 
133  if (oft == Cost_Function_mu)
134  {
135  w = 0.0;
136  }
137  if (oft == Cost_Function_musig)
138  {
139  w = 1.0;
140  }
141  if (oft == Cost_Function_weighted_musig)
142  {
143  w = (ValueType)m_WeightOfStandardDeviationTerm;
144  }
145 
146  const unsigned int n = areas.cols();
147 
148  // Temporary matrices H, K, L
149  DoubleMatrixType H(n, n, 0), K(n, n, 0), L(n, n, 0);
150  DoubleMatrixType H_RMSE(n, n, 0);
151  for (unsigned int i = 0; i < n; i++)
152  {
153  for (unsigned int j = 0; j < n; j++)
154  {
155  if (i == j)
156  {
157  // Diag (i=j)
158  for (unsigned int k = 0; k < n; k++)
159  {
160  if (i != k)
161  {
162  H[i][j] += areas[i][k] * (means[i][k] * means[i][k] + w * stds[i][k] * stds[i][k]);
163  K[i][j] += areas[i][k] * means[i][k];
164  L[i][j] += areas[i][k];
165  H_RMSE[i][j] += areas[i][k] * (means[i][k] * means[i][k] + stds[i][k] * stds[i][k]);
166  }
167  }
168  }
169  else
170  {
171  // Other than diag (i!=j)
172  H[i][j] = -areas[i][j] * (means[i][j] * means[j][i] + w * stds[i][j] * stds[j][i]);
173  K[i][j] = -areas[i][j] * means[i][j];
174  L[i][j] = -areas[i][j];
175  H_RMSE[i][j] = -areas[i][j] * mops[i][j];
176  }
177  }
178  }
179 
180  if (oft == Cost_Function_rmse)
181  {
182  H = H_RMSE;
183  }
184 
185  return H;
186 }
187 
188 /*
189  * Returns the sub-matrix of mat, composed only by rows/cols in idx
190  */
191 template <class ValueType>
194 {
195  unsigned int n = idx.size();
196  DoubleMatrixType newMat(n, n, 0);
197  for (unsigned int i = 0; i < n; i++)
198  {
199  for (unsigned int j = 0; j < n; j++)
200  {
201  unsigned int mat_i = idx[i];
202  unsigned int mat_j = idx[j];
203  newMat[i][j] = mat[mat_i][mat_j];
204  }
205  }
206  return newMat;
207 }
208 
209 /*
210  * QP Solving using vnl
211  */
212 template <class ValueType>
214 {
215  // Check matrices dimensions
216  CheckInputs();
217 
218  // Display a warning if overlap matrix is null
219  if (m_AreaInOverlaps.max_value() == 0)
220  {
221  itkExceptionMacro("No overlap in images!");
222  }
223 
224  // Identify the connected components
225  unsigned int nbOfComponents = m_AreaInOverlaps.rows();
226  unsigned int nextVertex = 0;
227  std::vector<ListIndexType> connectedComponentsIndices;
228  std::vector<bool> markedVertices(nbOfComponents, false);
229  bool allMarked = false;
230  while (!allMarked)
231  {
232  // Depth First Search starting from nextVertex
233  std::vector<bool> marked(nbOfComponents, false);
234  DFS(marked, nextVertex);
235 
236  // Id the connected component
237  ListIndexType list;
238  for (unsigned int i = 0; i < nbOfComponents; i++)
239  {
240  if (marked[i])
241  {
242  // Tag the connected component index
243  list.push_back(i);
244  markedVertices[i] = true;
245  }
246  else if (!markedVertices[i])
247  {
248  // if the i-th vertex is not marked, DFS will start from it next
249  nextVertex = i;
250  break;
251  }
252  }
253  connectedComponentsIndices.push_back(list);
254 
255  // Check if vertices are all marked
256  allMarked = true;
257  for (unsigned int i = 0; i < nbOfComponents; i++)
258  if (!markedVertices[i])
259  allMarked = false;
260  }
261 
262  // Prepare output model
263  m_OutputCorrectionModel.set_size(nbOfComponents);
264  m_OutputCorrectionModel.fill(itk::NumericTraits<ValueType>::One);
265 
266  // Extract and solve all connected components one by one
267  if (connectedComponentsIndices.size() > 1)
268  itkWarningMacro("Seems to be more that one group of overlapping images. Trying to harmonize groups separately.");
269 
270  for (unsigned int component = 0; component < connectedComponentsIndices.size(); component++)
271  {
272  // Indices list
273  ListIndexType list = connectedComponentsIndices[component];
274  const unsigned int n = list.size();
275 
276  // Extract matrices
277  DoubleMatrixType sub_areas = ExtractMatrix(m_AreaInOverlaps, list);
278  DoubleMatrixType sub_means = ExtractMatrix(m_MeanInOverlaps, list);
279  DoubleMatrixType sub_stdev = ExtractMatrix(m_StandardDeviationInOverlaps, list);
280  DoubleMatrixType sub_mOfPr = ExtractMatrix(m_MeanOfProductsInOverlaps, list);
281 
282  // Objective function
283  DoubleMatrixType Q = GetQuadraticObjectiveMatrix(sub_areas, sub_means, sub_stdev, sub_mOfPr);
284  DoubleVectorType g(n, 0);
285 
286  // Constraint (Energy conservation)
287  DoubleMatrixType A(1, n);
288  DoubleVectorType b(1, 0);
289  for (unsigned int i = 0; i < n; i++)
290  {
291  double energy = sub_areas[i][i] * sub_means[i][i];
292  b[0] += energy;
293  A[0][i] = energy;
294  }
295 
296  // Solution
297  DoubleVectorType x(n, 1);
298 
299  // Change tol. to 0.01 is a quick hack to avoid numerical instability...
300  bool solv = vnl_solve_qp_with_non_neg_constraints(Q, g, A, b, x, 0.01);
301  if (solv)
302  {
303  for (unsigned int i = 0; i < n; i++)
304  {
305  m_OutputCorrectionModel[list[i]] = static_cast<double>(x[i]);
306  }
307  }
308  else
309  {
310  itkWarningMacro("vnl_solve_qp_with_non_neg_constraints failed for component #" << component);
311  }
312  }
313 }
314 }
315 
316 #endif
otb::QuadraticallyConstrainedSimpleSolver::~QuadraticallyConstrainedSimpleSolver
virtual ~QuadraticallyConstrainedSimpleSolver()
Definition: otbQuadraticallyConstrainedSimpleSolver.hxx:38
otb::QuadraticallyConstrainedSimpleSolver::GetQuadraticObjectiveMatrix
const DoubleMatrixType GetQuadraticObjectiveMatrix(const DoubleMatrixType &areas, const DoubleMatrixType &means, const DoubleMatrixType &stds, const DoubleMatrixType &mops)
Definition: otbQuadraticallyConstrainedSimpleSolver.hxx:127
otb::QuadraticallyConstrainedSimpleSolver::Solve
void Solve()
Definition: otbQuadraticallyConstrainedSimpleSolver.hxx:213
otb::QuadraticallyConstrainedSimpleSolver::DoubleMatrixType
vnl_matrix< double > DoubleMatrixType
Definition: otbQuadraticallyConstrainedSimpleSolver.h:84
otb::QuadraticallyConstrainedSimpleSolver::QuadraticallyConstrainedSimpleSolver
QuadraticallyConstrainedSimpleSolver()
Definition: otbQuadraticallyConstrainedSimpleSolver.hxx:31
otb
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
Definition: otbJoinContainer.h:32
otb::QuadraticallyConstrainedSimpleSolver::DoubleVectorType
vnl_vector< double > DoubleVectorType
Definition: otbQuadraticallyConstrainedSimpleSolver.h:85
otb::QuadraticallyConstrainedSimpleSolver::ListIndexType
std::vector< unsigned int > ListIndexType
Definition: otbQuadraticallyConstrainedSimpleSolver.h:86
otb::QuadraticallyConstrainedSimpleSolver::DFS
void DFS(std::vector< bool > &marked, unsigned int s) const
Definition: otbQuadraticallyConstrainedSimpleSolver.hxx:49
otbQuadraticallyConstrainedSimpleSolver.h
otb::QuadraticallyConstrainedSimpleSolver::ExtractMatrix
const DoubleMatrixType ExtractMatrix(const RealMatrixType &mat, const ListIndexType &idx)
Definition: otbQuadraticallyConstrainedSimpleSolver.hxx:193
otb::QuadraticallyConstrainedSimpleSolver::CheckInputs
void CheckInputs(void) const
Definition: otbQuadraticallyConstrainedSimpleSolver.hxx:69
otb::QuadraticallyConstrainedSimpleSolver::RealMatrixType
vnl_matrix< ValueType > RealMatrixType
Definition: otbQuadraticallyConstrainedSimpleSolver.h:79