Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/core/src/downhill_simplex.cpp
16337 views
1
/*M///////////////////////////////////////////////////////////////////////////////////////
2
//
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4
//
5
// By downloading, copying, installing or using the software you agree to this license.
6
// If you do not agree to this license, do not download, install,
7
// copy or use the software.
8
//
9
//
10
// License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
14
// Third party copyrights are property of their respective owners.
15
//
16
// Redistribution and use in source and binary forms, with or without modification,
17
// are permitted provided that the following conditions are met:
18
//
19
// * Redistribution's of source code must retain the above copyright notice,
20
// this list of conditions and the following disclaimer.
21
//
22
// * Redistribution's in binary form must reproduce the above copyright notice,
23
// this list of conditions and the following disclaimer in the documentation
24
// and/or other materials provided with the distribution.
25
//
26
// * The name of the copyright holders may not be used to endorse or promote products
27
// derived from this software without specific prior written permission.
28
//
29
// This software is provided by the copyright holders and contributors "as is" and
30
// any express or implied warranties, including, but not limited to, the implied
31
// warranties of merchantability and fitness for a particular purpose are disclaimed.
32
// In no event shall the OpenCV Foundation or contributors be liable for any direct,
33
// indirect, incidental, special, exemplary, or consequential damages
34
// (including, but not limited to, procurement of substitute goods or services;
35
// loss of use, data, or profits; or business interruption) however caused
36
// and on any theory of liability, whether in contract, strict liability,
37
// or tort (including negligence or otherwise) arising in any way out of
38
// the use of this software, even if advised of the possibility of such damage.
39
//
40
//M*/
41
#include "precomp.hpp"
42
43
#if 0
44
#define dprintf(x) printf x
45
#define print_matrix(x) print(x)
46
#else
47
#define dprintf(x)
48
#define print_matrix(x)
49
#endif
50
51
/*
52
53
****Error Message********************************************************************************************************************
54
55
Downhill Simplex method in OpenCV dev 3.0.0 getting this error:
56
57
OpenCV Error: Assertion failed (dims <= 2 && data && (unsigned)i0 < (unsigned)(s ize.p[0] * size.p[1])
58
&& elemSize() == (((((DataType<_Tp>::type) & ((512 - 1) << 3)) >> 3) + 1) << ((((sizeof(size_t)/4+1)16384|0x3a50)
59
>> ((DataType<_Tp>::typ e) & ((1 << 3) - 1))2) & 3))) in Mat::at,
60
file C:\builds\master_PackSlave-w in32-vc12-shared\opencv\modules\core\include\opencv2/core/mat.inl.hpp, line 893
61
62
****Problem and Possible Fix*********************************************************************************************************
63
64
DownhillSolverImpl::innerDownhillSimplex something looks broken here:
65
Mat_<double> coord_sum(1,ndim,0.0),buf(1,ndim,0.0),y(1,ndim,0.0);
66
fcount = 0;
67
for(i=0;i<ndim+1;++i)
68
{
69
y(i) = f->calc(p[i]);
70
}
71
72
y has only ndim elements, while the loop goes over ndim+1
73
74
Edited the following for possible fix:
75
76
Replaced y(1,ndim,0.0) ------> y(1,ndim+1,0.0)
77
78
***********************************************************************************************************************************
79
80
The code below was used in tesing the source code.
81
Created by @SareeAlnaghy
82
83
#include <iostream>
84
#include <cstdlib>
85
#include <cmath>
86
#include <algorithm>
87
#include <opencv2\optim\optim.hpp>
88
89
using namespace std;
90
using namespace cv;
91
92
void test(Ptr<optim::DownhillSolver> MinProblemSolver, Ptr<optim::MinProblemSolver::Function> ptr_F, Mat &P, Mat &step)
93
{
94
try{
95
96
MinProblemSolver->setFunction(ptr_F);
97
MinProblemSolver->setInitStep(step);
98
double res = MinProblemSolver->minimize(P);
99
100
cout << "res " << res << endl;
101
}
102
catch (exception e)
103
{
104
cerr << "Error:: " << e.what() << endl;
105
}
106
}
107
108
int main()
109
{
110
111
class DistanceToLines :public optim::MinProblemSolver::Function {
112
public:
113
double calc(const double* x)const{
114
115
return x[0] * x[0] + x[1] * x[1];
116
117
}
118
};
119
120
Mat P = (Mat_<double>(1, 2) << 1.0, 1.0);
121
Mat step = (Mat_<double>(2, 1) << -0.5, 0.5);
122
123
Ptr<optim::MinProblemSolver::Function> ptr_F(new DistanceToLines());
124
Ptr<optim::DownhillSolver> MinProblemSolver = optim::createDownhillSolver();
125
126
test(MinProblemSolver, ptr_F, P, step);
127
128
system("pause");
129
return 0;
130
}
131
132
****Suggestion for improving Simplex implementation***************************************************************************************
133
134
Currently the downhilll simplex method outputs the function value that is minimized. It should also return the coordinate points where the
135
function is minimized. This is very useful in many applications such as using back projection methods to find a point of intersection of
136
multiple lines in three dimensions as not all lines intersect in three dimensions.
137
138
*/
139
140
namespace cv
141
{
142
143
class DownhillSolverImpl CV_FINAL : public DownhillSolver
144
{
145
public:
146
DownhillSolverImpl()
147
{
148
_Function=Ptr<Function>();
149
_step=Mat_<double>();
150
}
151
152
void getInitStep(OutputArray step) const CV_OVERRIDE { _step.copyTo(step); }
153
void setInitStep(InputArray step) CV_OVERRIDE
154
{
155
// set dimensionality and make a deep copy of step
156
Mat m = step.getMat();
157
dprintf(("m.cols=%d\nm.rows=%d\n", m.cols, m.rows));
158
if( m.rows == 1 )
159
m.copyTo(_step);
160
else
161
transpose(m, _step);
162
}
163
164
Ptr<MinProblemSolver::Function> getFunction() const CV_OVERRIDE { return _Function; }
165
166
void setFunction(const Ptr<Function>& f) CV_OVERRIDE { _Function=f; }
167
168
TermCriteria getTermCriteria() const CV_OVERRIDE { return _termcrit; }
169
170
void setTermCriteria( const TermCriteria& termcrit ) CV_OVERRIDE
171
{
172
CV_Assert( termcrit.type == (TermCriteria::MAX_ITER + TermCriteria::EPS) &&
173
termcrit.epsilon > 0 &&
174
termcrit.maxCount > 0 );
175
_termcrit=termcrit;
176
}
177
178
double minimize( InputOutputArray x_ ) CV_OVERRIDE
179
{
180
dprintf(("hi from minimize\n"));
181
CV_Assert( !_Function.empty() );
182
CV_Assert( std::min(_step.cols, _step.rows) == 1 &&
183
std::max(_step.cols, _step.rows) >= 2 &&
184
_step.type() == CV_64FC1 );
185
dprintf(("termcrit:\n\ttype: %d\n\tmaxCount: %d\n\tEPS: %g\n",_termcrit.type,_termcrit.maxCount,_termcrit.epsilon));
186
dprintf(("step\n"));
187
print_matrix(_step);
188
189
Mat x = x_.getMat(), simplex;
190
191
createInitialSimplex(x, simplex, _step);
192
int count = 0;
193
double res = innerDownhillSimplex(simplex,_termcrit.epsilon, _termcrit.epsilon,
194
count, _termcrit.maxCount);
195
dprintf(("%d iterations done\n",count));
196
197
if( !x.empty() )
198
{
199
Mat simplex_0m(x.rows, x.cols, CV_64F, simplex.ptr<double>());
200
simplex_0m.convertTo(x, x.type());
201
}
202
else
203
{
204
int x_type = x_.fixedType() ? x_.type() : CV_64F;
205
simplex.row(0).convertTo(x_, x_type);
206
}
207
return res;
208
}
209
protected:
210
Ptr<MinProblemSolver::Function> _Function;
211
TermCriteria _termcrit;
212
Mat _step;
213
214
inline void updateCoordSum(const Mat& p, Mat& coord_sum)
215
{
216
int i, j, m = p.rows, n = p.cols;
217
double* coord_sum_ = coord_sum.ptr<double>();
218
CV_Assert( coord_sum.cols == n && coord_sum.rows == 1 );
219
220
for( j = 0; j < n; j++ )
221
coord_sum_[j] = 0.;
222
223
for( i = 0; i < m; i++ )
224
{
225
const double* p_i = p.ptr<double>(i);
226
for( j = 0; j < n; j++ )
227
coord_sum_[j] += p_i[j];
228
}
229
230
dprintf(("\nupdated coord sum:\n"));
231
print_matrix(coord_sum);
232
233
}
234
235
inline void createInitialSimplex( const Mat& x0, Mat& simplex, Mat& step )
236
{
237
int i, j, ndim = step.cols;
238
CV_Assert( _Function->getDims() == ndim );
239
Mat x = x0;
240
if( x0.empty() )
241
x = Mat::zeros(1, ndim, CV_64F);
242
CV_Assert( (x.cols == 1 && x.rows == ndim) || (x.cols == ndim && x.rows == 1) );
243
CV_Assert( x.type() == CV_32F || x.type() == CV_64F );
244
245
simplex.create(ndim + 1, ndim, CV_64F);
246
Mat simplex_0m(x.rows, x.cols, CV_64F, simplex.ptr<double>());
247
248
x.convertTo(simplex_0m, CV_64F);
249
double* simplex_0 = simplex.ptr<double>();
250
const double* step_ = step.ptr<double>();
251
for( i = 1; i <= ndim; i++ )
252
{
253
double* simplex_i = simplex.ptr<double>(i);
254
for( j = 0; j < ndim; j++ )
255
simplex_i[j] = simplex_0[j];
256
simplex_i[i-1] += 0.5*step_[i-1];
257
}
258
for( j = 0; j < ndim; j++ )
259
simplex_0[j] -= 0.5*step_[j];
260
261
dprintf(("\nthis is simplex\n"));
262
print_matrix(simplex);
263
}
264
265
/*
266
Performs the actual minimization of MinProblemSolver::Function f (after the initialization was done)
267
268
The matrix p[ndim+1][1..ndim] represents ndim+1 vertices that
269
form a simplex - each row is an ndim vector.
270
On output, fcount gives the number of function evaluations taken.
271
*/
272
double innerDownhillSimplex( Mat& p, double MinRange, double MinError, int& fcount, int nmax )
273
{
274
int i, j, ndim = p.cols;
275
Mat coord_sum(1, ndim, CV_64F), buf(1, ndim, CV_64F), y(1, ndim+1, CV_64F);
276
double* y_ = y.ptr<double>();
277
278
fcount = ndim+1;
279
for( i = 0; i <= ndim; i++ )
280
y_[i] = calc_f(p.ptr<double>(i));
281
282
updateCoordSum(p, coord_sum);
283
284
for (;;)
285
{
286
// find highest (worst), next-to-worst, and lowest
287
// (best) points by going through all of them.
288
int ilo = 0, ihi, inhi;
289
if( y_[0] > y_[1] )
290
{
291
ihi = 0; inhi = 1;
292
}
293
else
294
{
295
ihi = 1; inhi = 0;
296
}
297
for( i = 0; i <= ndim; i++ )
298
{
299
double yval = y_[i];
300
if (yval <= y_[ilo])
301
ilo = i;
302
if (yval > y_[ihi])
303
{
304
inhi = ihi;
305
ihi = i;
306
}
307
else if (yval > y_[inhi] && i != ihi)
308
inhi = i;
309
}
310
CV_Assert( ihi != inhi );
311
if( ilo == inhi || ilo == ihi )
312
{
313
for( i = 0; i <= ndim; i++ )
314
{
315
double yval = y_[i];
316
if( yval == y_[ilo] && i != ihi && i != inhi )
317
{
318
ilo = i;
319
break;
320
}
321
}
322
}
323
dprintf(("\nthis is y on iteration %d:\n",fcount));
324
print_matrix(y);
325
326
// check stop criterion
327
double error = fabs(y_[ihi] - y_[ilo]);
328
double range = 0;
329
for( j = 0; j < ndim; j++ )
330
{
331
double minval, maxval;
332
minval = maxval = p.at<double>(0, j);
333
for( i = 1; i <= ndim; i++ )
334
{
335
double pval = p.at<double>(i, j);
336
minval = std::min(minval, pval);
337
maxval = std::max(maxval, pval);
338
}
339
range = std::max(range, fabs(maxval - minval));
340
}
341
342
if( range <= MinRange || error <= MinError || fcount >= nmax )
343
{
344
// Put best point and value in first slot.
345
std::swap(y_[0], y_[ilo]);
346
for( j = 0; j < ndim; j++ )
347
{
348
std::swap(p.at<double>(0, j), p.at<double>(ilo, j));
349
}
350
break;
351
}
352
353
double y_lo = y_[ilo], y_nhi = y_[inhi], y_hi = y_[ihi];
354
// Begin a new iteration. First, reflect the worst point about the centroid of others
355
double alpha = -1.0;
356
double y_alpha = tryNewPoint(p, coord_sum, ihi, alpha, buf, fcount);
357
358
dprintf(("\ny_lo=%g, y_nhi=%g, y_hi=%g, y_alpha=%g, p_alpha:\n", y_lo, y_nhi, y_hi, y_alpha));
359
print_matrix(buf);
360
361
if( y_alpha < y_nhi )
362
{
363
if( y_alpha < y_lo )
364
{
365
// If that's better than the best point, go twice as far in that direction
366
double beta = -2.0;
367
double y_beta = tryNewPoint(p, coord_sum, ihi, beta, buf, fcount);
368
dprintf(("\ny_beta=%g, p_beta:\n", y_beta));
369
print_matrix(buf);
370
if( y_beta < y_alpha )
371
{
372
alpha = beta;
373
y_alpha = y_beta;
374
}
375
}
376
replacePoint(p, coord_sum, y, ihi, alpha, y_alpha);
377
}
378
else
379
{
380
// The new point is worse than the second-highest,
381
// do not go so far in that direction
382
double gamma = 0.5;
383
double y_gamma = tryNewPoint(p, coord_sum, ihi, gamma, buf, fcount);
384
dprintf(("\ny_gamma=%g, p_gamma:\n", y_gamma));
385
print_matrix(buf);
386
if( y_gamma < y_hi )
387
replacePoint(p, coord_sum, y, ihi, gamma, y_gamma);
388
else
389
{
390
// Can't seem to improve things.
391
// Contract the simplex to good point
392
// in hope to find a simplex landscape.
393
for( i = 0; i <= ndim; i++ )
394
{
395
if (i != ilo)
396
{
397
for( j = 0; j < ndim; j++ )
398
p.at<double>(i, j) = 0.5*(p.at<double>(i, j) + p.at<double>(ilo, j));
399
y_[i] = calc_f(p.ptr<double>(i));
400
}
401
}
402
fcount += ndim;
403
updateCoordSum(p, coord_sum);
404
}
405
}
406
dprintf(("\nthis is simplex on iteration %d\n",fcount));
407
print_matrix(p);
408
}
409
return y_[0];
410
}
411
412
inline double calc_f(const double* ptr)
413
{
414
double res = _Function->calc(ptr);
415
CV_Assert( !cvIsNaN(res) && !cvIsInf(res) );
416
return res;
417
}
418
419
double tryNewPoint( Mat& p, Mat& coord_sum, int ihi, double alpha_, Mat& ptry, int& fcount )
420
{
421
int j, ndim = p.cols;
422
423
double alpha = (1.0 - alpha_)/ndim;
424
double beta = alpha - alpha_;
425
double* p_ihi = p.ptr<double>(ihi);
426
double* ptry_ = ptry.ptr<double>();
427
double* coord_sum_ = coord_sum.ptr<double>();
428
429
for( j = 0; j < ndim; j++ )
430
ptry_[j] = coord_sum_[j]*alpha - p_ihi[j]*beta;
431
432
fcount++;
433
return calc_f(ptry_);
434
}
435
436
void replacePoint( Mat& p, Mat& coord_sum, Mat& y, int ihi, double alpha_, double ytry )
437
{
438
int j, ndim = p.cols;
439
440
double alpha = (1.0 - alpha_)/ndim;
441
double beta = alpha - alpha_;
442
double* p_ihi = p.ptr<double>(ihi);
443
double* coord_sum_ = coord_sum.ptr<double>();
444
445
for( j = 0; j < ndim; j++ )
446
p_ihi[j] = coord_sum_[j]*alpha - p_ihi[j]*beta;
447
y.at<double>(ihi) = ytry;
448
updateCoordSum(p, coord_sum);
449
}
450
};
451
452
453
// both minRange & minError are specified by termcrit.epsilon;
454
// In addition, user may specify the number of iterations that the algorithm does.
455
Ptr<DownhillSolver> DownhillSolver::create( const Ptr<MinProblemSolver::Function>& f,
456
InputArray initStep, TermCriteria termcrit )
457
{
458
Ptr<DownhillSolver> DS = makePtr<DownhillSolverImpl>();
459
DS->setFunction(f);
460
DS->setInitStep(initStep);
461
DS->setTermCriteria(termcrit);
462
return DS;
463
}
464
465
}
466
467