LevMarFitting.h
Go to the documentation of this file.
1#ifndef LEVMARFITTING_HEADER
2#define LEVMARFITTING_HEADER
3#include <algorithm>
4#include <cmath>
5#include <cstring>
6#include <iostream>
7
8#include "LevMarFunc.h"
9#include "Vector.h"
10#ifdef DOPARALLEL
11#include <omp.h>
12#endif
13
14template <class ScalarT>
15bool
16Cholesky(ScalarT* a, size_t n, ScalarT p[])
17/*Given a positive-definite symmetric matrix a[1..n][1..n], this routine constructs its Cholesky
18decomposition, A = L � LT . On input, only the upper triangle of a need be given; it is not
19modified. The Cholesky factor L is returned in the lower triangle of a, except for its diagonal
20elements which are returned in p[1..n].*/
21{
22 size_t i, j, k;
23 ScalarT sum;
24 for (i = 0; i < n; ++i)
25 {
26 for (j = i; j < n; ++j)
27 {
28 for (sum = a[i * n + j], k = i - 1; k != -1; --k)
29 {
30 sum -= a[i * n + k] * a[j * n + k];
31 }
32 if (i == j)
33 {
34 if (sum <= ScalarT(0))
35 // a, with rounding errors, is not positive definite.
36 {
37 return false;
38 }
39 p[i] = std::sqrt(sum);
40 }
41 else
42 {
43 a[j * n + i] = sum / p[i];
44 }
45 }
46 }
47 return true;
48}
49
50template <class ScalarT, unsigned int N>
51bool
52Cholesky(ScalarT* a, ScalarT p[])
53/*Given a positive-definite symmetric matrix a[1..n][1..n], this routine constructs its Cholesky
54decomposition, A = L � LT . On input, only the upper triangle of a need be given; it is not
55modified. The Cholesky factor L is returned in the lower triangle of a, except for its diagonal
56elements which are returned in p[1..n].*/
57{
58 size_t i, j, k;
59 ScalarT sum;
60 for (i = 0; i < N; ++i)
61 {
62 for (j = i; j < N; ++j)
63 {
64 for (sum = a[i * N + j], k = i - 1; k != -1; --k)
65 {
66 sum -= a[i * N + k] * a[j * N + k];
67 }
68 if (i == j)
69 {
70 if (sum <= ScalarT(0))
71 // a, with rounding errors, is not positive definite.
72 {
73 return false;
74 }
75 p[i] = std::sqrt(sum);
76 }
77 else
78 {
79 a[j * N + i] = sum / p[i];
80 }
81 }
82 }
83 return true;
84}
85
86template <class ScalarT>
87void
88CholeskySolve(ScalarT* a, size_t n, ScalarT p[], ScalarT b[], ScalarT x[])
89/*Solves the set of n linear equations A � x = b, where a is a positive-definite symmetric matrix.
90a[1..n][1..n] and p[1..n] are input as the output of the routine choldc. Only the lower
91subdiagonal portion of a is accessed. b[1..n] is input as the right-hand side vector. The
92solution vector is returned in x[1..n]. a, n, and p are not modified and can be left in place
93for successive calls with different right-hand sides b. b is not modified unless you identify b and
94x in the calling sequence, which is allowed.*/
95{
96 size_t i, k;
97 ScalarT sum;
98 for (i = 0; i < n; i++)
99 {
100 // Solve L � y = b, storing y in x.
101 for (sum = b[i], k = i - 1; k != -1; --k)
102 {
103 sum -= a[i * n + k] * x[k];
104 }
105 x[i] = sum / p[i];
106 }
107 for (i = n - 1; i != -1; --i)
108 {
109 // Solve LT � x = y.
110 for (sum = x[i], k = i + 1; k < n; ++k)
111 {
112 sum -= a[k * n + i] * x[k];
113 }
114 x[i] = sum / p[i];
115 }
116}
117
118template <class ScalarT, unsigned int N>
119void
120CholeskySolve(ScalarT* a, ScalarT p[], ScalarT b[], ScalarT x[])
121/*Solves the set of n linear equations A � x = b, where a is a positive-definite symmetric matrix.
122a[1..n][1..n] and p[1..n] are input as the output of the routine choldc. Only the lower
123subdiagonal portion of a is accessed. b[1..n] is input as the right-hand side vector. The
124solution vector is returned in x[1..n]. a, n, and p are not modified and can be left in place
125for successive calls with different right-hand sides b. b is not modified unless you identify b and
126x in the calling sequence, which is allowed.*/
127{
128 size_t i, k;
129 ScalarT sum;
130 for (i = 0; i < N; i++)
131 {
132 // Solve L � y = b, storing y in x.
133 for (sum = b[i], k = i - 1; k != -1; --k)
134 {
135 sum -= a[i * N + k] * x[k];
136 }
137 x[i] = sum / p[i];
138 }
139 for (i = N - 1; i != -1; --i)
140 {
141 // Solve LT � x = y.
142 for (sum = x[i], k = i + 1; k < N; ++k)
143 {
144 sum -= a[k * N + i] * x[k];
145 }
146 x[i] = sum / p[i];
147 }
148}
149
150template <class IteratorT, class FuncT>
151bool
152LevMar(IteratorT begin, IteratorT end, FuncT& func, typename FuncT::ScalarType* param)
153{
154 typedef typename FuncT::ScalarType ScalarType;
155
156 enum
157 {
158 paramDim = FuncT::NumParams
159 };
160
161 bool retVal = true;
162 unsigned int totalSize = end - begin;
163 if (!totalSize)
164 {
165 return false;
166 }
167 ScalarType lambda = ScalarType(0.0001);
168 ScalarType* F0 = new ScalarType[totalSize * paramDim];
169 ScalarType* U = new ScalarType[paramDim * paramDim];
170 ScalarType* H = new ScalarType[paramDim * paramDim];
171 ScalarType* v = new ScalarType[paramDim];
172 ScalarType* d = new ScalarType[totalSize];
173 ScalarType* temp = new ScalarType[totalSize];
174 ScalarType* x = new ScalarType[paramDim];
175 ScalarType* p = new ScalarType[paramDim];
176 ScalarType* paramNew = new ScalarType[paramDim];
177 size_t nu = 2;
178 func.Normalize(param);
179 ScalarType paramNorm = 0;
180
181 // do fitting in different steps
182 unsigned int subsets =
183 std::max(int(std::floor(std::log((float)totalSize) / std::log(2.f))) - 8, 1);
184#ifdef PRECISIONLEVMAR
185 subsets = 1;
186#endif
187 MiscLib::Vector<unsigned int> subsetSizes(subsets);
188 for (unsigned int i = subsetSizes.size(); i;)
189 {
190 --i;
191 subsetSizes[i] = totalSize;
192 if (i)
193 {
194 subsetSizes[i] = subsetSizes[i] >> 1;
195 }
196 totalSize -= subsetSizes[i];
197 }
198 unsigned int curSubset = 0;
199 unsigned int size = 0;
200
201 // get current error
202 ScalarType chi = 0, newChi = 0;
203 ScalarType rho = 1;
204 unsigned int outerIter = 0,
205#ifndef PRECISIONLEVMAR
206 maxOuterIter = 200 / subsetSizes.size(),
207#else
208 maxOuterIter = 500,
209#endif
210 usefulIter = 0, totalIter = 0;
211 ;
212 do
213 {
214 // get current error
215 size += subsetSizes[curSubset];
216 newChi = func.Chi(param, begin, begin + size, d, temp);
217 for (unsigned int i = 0; i < paramDim; ++i)
218 {
219 paramNew[i] = param[i];
220 }
221 outerIter = 0;
222 if (rho < 0)
223 {
224 rho = 1;
225 }
226 do
227 {
228 ++outerIter;
229 ++totalIter;
230 if (rho > 0)
231 {
232 nu = 2;
233 chi = newChi;
234 for (size_t i = 0; i < paramDim; ++i)
235 {
236 param[i] = paramNew[i];
237 }
238#ifndef PRECISIONLEVMAR
239 if (std::sqrt(chi / size) <
240 ScalarType(1e-5)) // chi very small? -> will be hard to improve
241 {
242 //std::cout << "LevMar converged because of small chi" << std::endl;
243 break;
244 }
245#endif
246 paramNorm = 0;
247 for (size_t i = 0; i < paramDim; ++i)
248 {
249 paramNorm += param[i] * param[i];
250 }
251 paramNorm = std::sqrt(paramNorm);
252 // construct the needed matrices
253 // F0 is the matrix constructed from param
254 // F0 has gradient_i(param) as its ith row
255 func.Derivatives(param, begin, begin + size, d, temp, F0);
256// U = F0_t * F0
257// v = F0_t * d(param) (d(param) = [d_i(param)])
258#pragma omp parallel for
259 for (int i = 0; i < paramDim; ++i)
260 {
261 for (size_t j = i; j < paramDim;
262 ++j) // j = i since only upper triangle is needed
263 {
264 U[i * paramDim + j] = 0;
265 for (size_t k = 0; k < size; ++k)
266 {
267 U[i * paramDim + j] += F0[k * paramDim + i] * F0[k * paramDim + j];
268 }
269 }
270 }
271 ScalarType vmag = 0; // magnitude of v
272#pragma omp parallel for
273 for (int i = 0; i < paramDim; ++i)
274 {
275 v[i] = 0;
276 for (size_t k = 0; k < size; ++k)
277 {
278 v[i] += F0[k * paramDim + i] * d[k];
279 }
280 v[i] *= -1;
281#ifndef DOPARALLEL
282 vmag = std::max(abs(v[i]), vmag);
283#endif
284 }
285#ifdef DOPARALLEL
286 for (unsigned int i = 0; i < paramDim; ++i)
287 {
288 vmag = std::max(abs(v[i]), vmag);
289 }
290#endif
291 // and check for convergence with magnitude of v
292#ifndef PRECISIONLEVMAR
293 if (vmag < ScalarType(1e-6))
294#else
295 if (vmag < ScalarType(1e-8))
296#endif
297 {
298 //std::cout << "LevMar converged with small gradient" << std::endl;
299 //retVal = chi < initialChi;
300 //goto cleanup;
301 break;
302 }
303 if (outerIter == 1)
304 {
305 // compute magnitue of F0
306 ScalarType fmag = abs(F0[0]);
307 for (size_t i = 1; i < paramDim * size; ++i)
308 if (fmag < abs(F0[i]))
309 {
310 fmag = abs(F0[i]);
311 }
312 lambda = 1e-3f * fmag;
313 }
314 else
315 {
316 lambda *= std::max(ScalarType(.3), ScalarType(1 - std::pow(2 * rho - 1, 3)));
317 }
318 }
319
320 std::memcpy(H, U, sizeof(ScalarType) * paramDim * paramDim);
321 for (size_t i = 0; i < paramDim; ++i)
322 {
323 H[i * paramDim + i] += lambda; // * (ScalarType(1) + H[i * paramDim + i]);
324 }
325 // now H is positive definite and symmetric
326 // solve Hx = -v with Cholesky
327 ScalarType xNorm = 0, L = 0;
329 {
330 goto increment;
331 }
333
334 // magnitude of x small? If yes we are done
335 for (size_t i = 0; i < paramDim; ++i)
336 {
337 xNorm += x[i] * x[i];
338 }
339 xNorm = std::sqrt(xNorm);
340#ifndef PRECISIONLEVMAR
341 if (xNorm <= ScalarType(1e-6) * (paramNorm + ScalarType(1e-6)))
342#else
343 if (xNorm <= ScalarType(1e-8) * (paramNorm + ScalarType(1e-8)))
344#endif
345 {
346 //std::cout << "LevMar converged with small step" << std::endl;
347 //goto cleanup;
348 break;
349 }
350
351 for (size_t i = 0; i < paramDim; ++i)
352 {
353 paramNew[i] = param[i] + x[i];
354 }
355 func.Normalize(paramNew);
356
357 // get new error
358 newChi = func.Chi(paramNew, begin, begin + size, d, temp);
359
360 // the following test is taken from
361 // "Methods for non-linear least squares problems"
362 // by Madsen, Nielsen, Tingleff
363 L = 0;
364 for (size_t i = 0; i < paramDim; ++i)
365 {
366 L += .5f * x[i] * (lambda * x[i] + v[i]);
367 }
368 rho = (chi - newChi) / L;
369 if (rho > 0)
370 {
371 ++usefulIter;
372#ifndef PRECISIONLEVMAR
373 if ((chi - newChi) < 1e-4 * chi)
374 {
375 //std::cout << "LevMar converged with small chi difference" << std::endl;
376 chi = newChi;
377 for (size_t i = 0; i < paramDim; ++i)
378 {
379 param[i] = paramNew[i];
380 }
381 break;
382 }
383#endif
384 continue;
385 }
386
387 increment:
388 rho = -1;
389 // increment lambda
390 lambda = nu * lambda;
391 size_t nu2 = nu << 1;
392 if (nu2 < nu)
393 {
394 nu2 = 2;
395 }
396 nu = nu2;
397 } while (outerIter < maxOuterIter);
398 ++curSubset;
399 } while (curSubset < subsetSizes.size());
400 retVal = usefulIter > 0;
401 delete[] F0;
402 delete[] U;
403 delete[] H;
404 delete[] v;
405 delete[] d;
406 delete[] temp;
407 delete[] x;
408 delete[] p;
409 delete[] paramNew;
410 return retVal;
411}
412
413template <class ScalarT>
414ScalarT
415LevMar(unsigned int paramDim,
416 unsigned int imgDim,
417 const LevMarFunc<ScalarT>** funcs,
418 ScalarT* param)
419{
420 ScalarT retVal = -1;
421 size_t size = imgDim;
422 float lambda = ScalarT(0.0001);
423 ScalarT* F0 = new ScalarT[size * paramDim];
424 ScalarT* U = new ScalarT[paramDim * paramDim];
425 ScalarT* H = new ScalarT[paramDim * paramDim];
426 ScalarT* v = new ScalarT[paramDim];
427 ScalarT* d = new ScalarT[size];
428 ScalarT* dNew = new ScalarT[size];
429 ScalarT* x = new ScalarT[paramDim];
430 ScalarT* p = new ScalarT[paramDim];
431 ScalarT* paramNew = new ScalarT[paramDim];
432 size_t outerIter = 0, maxOuterIter = 10;
433 // get current error
434 ScalarT chi = 0, newChi;
435 for (size_t i = 0; i < size; ++i)
436 {
437 d[i] = (*(funcs[i]))(param);
438 chi += d[i] * d[i];
439 }
440 do
441 {
442 ++outerIter;
443 lambda *= ScalarT(0.04);
444 // construct the needed matrices
445 // F0 is the matrix constructed from param
446 // F0 has gradient_i(param) as its ith row
447 for (size_t i = 0; i < size; ++i)
448 {
449 (*(funcs[i]))(param, &F0[i * paramDim]);
450 }
451 // U = F0_t * F0
452 for (size_t i = 0; i < paramDim; ++i)
453 for (size_t j = i; j < paramDim; ++j) // j = i since only upper triangle is needed
454 {
455 U[i * paramDim + j] = 0;
456 for (size_t k = 0; k < size; ++k)
457 U[i * paramDim + j] += F0[k * paramDim + i] * F0[k * paramDim + j];
458 }
459 // v = F_t * d(param) (d(param) = [d_i(param)])
460 for (size_t i = 0; i < paramDim; ++i)
461 {
462 v[i] = 0;
463 for (size_t k = 0; k < size; ++k)
464 {
465 v[i] += F0[k * paramDim + i] * d[k];
466 }
467 v[i] *= -1;
468 }
469 size_t iter = 0, maxIter = 10;
470 do
471 {
472 ++iter;
473 // increment lambda
474 lambda = 10 * lambda;
475 memcpy(H, U, sizeof(ScalarT) * paramDim * paramDim);
476 for (size_t i = 0; i < paramDim; ++i)
477 {
478 H[i * paramDim + i] += lambda * (ScalarT(1) + H[i * paramDim + i]);
479 }
480 // now H is positive definite and symmetric
481 // solve Hx = -v with Cholesky
482 if (!Cholesky(H, paramDim, p))
483 {
484 goto cleanup;
485 }
486 CholeskySolve(H, paramDim, p, v, x);
487 for (size_t i = 0; i < paramDim; ++i)
488 {
489 paramNew[i] = param[i] + x[i];
490 }
491 // get new error
492 newChi = 0;
493 for (size_t i = 0; i < size; ++i)
494 {
495 dNew[i] = (*(funcs[i]))(paramNew);
496 newChi += dNew[i] * dNew[i];
497 }
498 // check for convergence
499 /*float cvgTest = 0;
500 for(size_t i = 0; i < paramDim; ++i)
501 {
502 //float c = param[i] - paramNew[i];
503 cvgTest += x[i] * x[i];
504 }
505 if(std::sqrt(cvgTest) < 1e-6)
506 {
507 for(size_t i = 0; i < paramDim; ++i)
508 param[i] = paramNew[i];
509 goto cleanup;
510 }*/
511 if (/*newChi <= chi &&*/ abs(chi - newChi) / chi < ScalarT(1e-4))
512 {
513 for (size_t i = 0; i < paramDim; ++i)
514 {
515 param[i] = paramNew[i];
516 }
517 retVal = newChi;
518 goto cleanup;
519 }
520 } while (newChi > chi && iter < maxIter);
521 if (newChi < chi)
522 {
523 chi = newChi;
524 for (size_t i = 0; i < paramDim; ++i)
525 {
526 param[i] = paramNew[i];
527 }
528 std::swap(d, dNew);
529 }
530 } while (outerIter < maxOuterIter);
531cleanup:
532 delete[] F0;
533 delete[] U;
534 delete[] H;
535 delete[] v;
536 delete[] d;
537 delete[] dNew;
538 delete[] x;
539 delete[] p;
540 delete[] paramNew;
541 return retVal;
542}
543
544#endif
void CholeskySolve(ScalarT *a, size_t n, ScalarT p[], ScalarT b[], ScalarT x[])
bool Cholesky(ScalarT *a, size_t n, ScalarT p[])
bool LevMar(IteratorT begin, IteratorT end, FuncT &func, typename FuncT::ScalarType *param)
size_type size() const
Definition Vector.h:215
This file offers overloads of toIce() and fromIce() functions for STL container types.