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 ****Suggesttion for imporving Simplex implentation***************************************************************************************
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 : public DownhillSolver
144 {
145 public:
DownhillSolverImpl()146 DownhillSolverImpl()
147 {
148 _Function=Ptr<Function>();
149 _step=Mat_<double>();
150 }
151
getInitStep(OutputArray step) const152 void getInitStep(OutputArray step) const { _step.copyTo(step); }
setInitStep(InputArray step)153 void setInitStep(InputArray step)
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
getFunction() const164 Ptr<MinProblemSolver::Function> getFunction() const { return _Function; }
165
setFunction(const Ptr<Function> & f)166 void setFunction(const Ptr<Function>& f) { _Function=f; }
167
getTermCriteria() const168 TermCriteria getTermCriteria() const { return _termcrit; }
169
setTermCriteria(const TermCriteria & termcrit)170 void setTermCriteria( const TermCriteria& termcrit )
171 {
172 CV_Assert( termcrit.type == (TermCriteria::MAX_ITER + TermCriteria::EPS) &&
173 termcrit.epsilon > 0 &&
174 termcrit.maxCount > 0 );
175 _termcrit=termcrit;
176 }
177
minimize(InputOutputArray x_)178 double minimize( InputOutputArray x_ )
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
updateCoordSum(const Mat & p,Mat & coord_sum)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
createInitialSimplex(const Mat & x0,Mat & simplex,Mat & step)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 */
innerDownhillSimplex(Mat & p,double MinRange,double MinError,int & fcount,int nmax)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
calc_f(const double * ptr)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
tryNewPoint(Mat & p,Mat & coord_sum,int ihi,double alpha_,Mat & ptry,int & fcount)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
replacePoint(Mat & p,Mat & coord_sum,Mat & y,int ihi,double alpha_,double ytry)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.
create(const Ptr<MinProblemSolver::Function> & f,InputArray initStep,TermCriteria termcrit)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