ml: fix SimulatedAnnealingSolver interface
authorAlexander Alekhin <alexander.alekhin@intel.com>
Fri, 15 Dec 2017 14:40:08 +0000 (17:40 +0300)
committerAlexander Alekhin <alexander.a.alekhin@gmail.com>
Fri, 15 Dec 2017 18:44:32 +0000 (21:44 +0300)
modules/ml/include/opencv2/ml.hpp
modules/ml/src/ann_mlp.cpp
modules/ml/test/test_mltests2.cpp
samples/cpp/travelsalesman.cpp

index 1ea79f2..c276bcf 100644 (file)
@@ -1528,6 +1528,9 @@ public:
     /** @copybrief getAnnealItePerStep @see getAnnealItePerStep */
     CV_WRAP void setAnnealItePerStep(int val);
 
+    /** @brief Set/initialize anneal RNG */
+    void setAnnealEnergyRNG(const RNG& rng);
+
     /** possible activation functions */
     enum ActivationFunctions {
         /** Identity function: \f$f(x)=x\f$ */
@@ -1875,86 +1878,104 @@ class CV_EXPORTS_W ANN_MLP_ANNEAL : public ANN_MLP
 {
 public:
     /** @see setAnnealInitialT */
-    CV_WRAP virtual double getAnnealInitialT() const;
+    CV_WRAP virtual double getAnnealInitialT() const = 0;
     /** @copybrief getAnnealInitialT @see getAnnealInitialT */
-    CV_WRAP virtual void setAnnealInitialT(double val);
+    CV_WRAP virtual void setAnnealInitialT(double val) = 0;
 
     /** ANNEAL: Update final temperature.
     It must be \>=0 and less than initialT. Default value is 0.1.*/
     /** @see setAnnealFinalT */
-    CV_WRAP  virtual double getAnnealFinalT() const;
+    CV_WRAP  virtual double getAnnealFinalT() const = 0;
     /** @copybrief getAnnealFinalT @see getAnnealFinalT */
-    CV_WRAP  virtual void setAnnealFinalT(double val);
+    CV_WRAP  virtual void setAnnealFinalT(double val) = 0;
 
     /** ANNEAL: Update cooling ratio.
     It must be \>0 and less than 1. Default value is 0.95.*/
     /** @see setAnnealCoolingRatio */
-    CV_WRAP  virtual double getAnnealCoolingRatio() const;
+    CV_WRAP  virtual double getAnnealCoolingRatio() const = 0;
     /** @copybrief getAnnealCoolingRatio @see getAnnealCoolingRatio */
-    CV_WRAP  virtual void setAnnealCoolingRatio(double val);
+    CV_WRAP  virtual void setAnnealCoolingRatio(double val) = 0;
 
     /** ANNEAL: Update iteration per step.
     It must be \>0 . Default value is 10.*/
     /** @see setAnnealItePerStep */
-    CV_WRAP virtual int getAnnealItePerStep() const;
+    CV_WRAP virtual int getAnnealItePerStep() const = 0;
     /** @copybrief getAnnealItePerStep @see getAnnealItePerStep */
-    CV_WRAP virtual  void setAnnealItePerStep(int val);
-
-
-    /** @brief Creates empty model
-
-    Use StatModel::train to train the model, Algorithm::load\<ANN_MLP\>(filename) to load the pre-trained model.
-    Note that the train method has optional flags: ANN_MLP::TrainFlags.
-    */
-//    CV_WRAP static Ptr<ANN_MLP> create();
+    CV_WRAP virtual void setAnnealItePerStep(int val) = 0;
 
+    /** @brief Set/initialize anneal RNG */
+    virtual void setAnnealEnergyRNG(const RNG& rng) = 0;
 };
 
+
 /****************************************************************************************\
 *                                   Simulated annealing solver                             *
 \****************************************************************************************/
 
+/** @brief The class defines interface for system state used in simulated annealing optimization algorithm.
+
+@cite Kirkpatrick83 for details
+*/
+class CV_EXPORTS SimulatedAnnealingSolverSystem
+{
+protected:
+    inline SimulatedAnnealingSolverSystem() {}
+public:
+    virtual ~SimulatedAnnealingSolverSystem() {}
+
+    /** Give energy value for a state of system.*/
+    virtual double energy() const = 0;
+    /** Function which change the state of system (random pertubation).*/
+    virtual void changeState() = 0;
+    /** Function to reverse to the previous state. Can be called once only after changeState(). */
+    virtual void reverseState() = 0;
+};
+
 /** @brief The class implements simulated annealing for optimization.
+ *
 @cite Kirkpatrick83 for details
 */
 class CV_EXPORTS SimulatedAnnealingSolver : public Algorithm
 {
 public:
-    SimulatedAnnealingSolver() { init(); }
-    ~SimulatedAnnealingSolver();
-    /** Give energy value for  a state of system.*/
-    virtual double energy() =0;
-    /** Function which change the state of system (random pertubation).*/
-    virtual void changedState() = 0;
-    /** Function to reverse to the previous state.*/
-    virtual void reverseChangedState() = 0;
-    /** Simulated annealing procedure.  */
+    SimulatedAnnealingSolver(const Ptr<SimulatedAnnealingSolverSystem>& system);
+    inline ~SimulatedAnnealingSolver() { release(); }
+
+    /** Simulated annealing procedure. */
     int run();
-    /** Set intial temperature of simulated annealing procedure.
-    *@param x new initial temperature. x\>0
+    /** Set/initialize RNG (energy).
+    @param rng new RNG
+    */
+    void setEnergyRNG(const RNG& rng);
+    /** Set initial temperature of simulated annealing procedure.
+    @param x new initial temperature. x\>0
     */
     void setInitialTemperature(double x);
     /** Set final temperature of simulated annealing procedure.
-    *@param x new final temperature value. 0\<x\<initial temperature
+    @param x new final temperature value. 0\<x\<initial temperature
     */
     void setFinalTemperature(double x);
+    /** Get final temperature of simulated annealing procedure. */
     double getFinalTemperature();
     /** Set setCoolingRatio of simulated annealing procedure : T(t) = coolingRatio * T(t-1).
-    @param x new cooling ratio value. 0\<x\<1
+    @param x new cooling ratio value. 0\<x\<1
     */
     void setCoolingRatio(double x);
     /** Set number iteration per temperature step.
-    @param ite number of iteration per temperature step ite \> 0
+    @param ite number of iteration per temperature step ite \> 0
     */
     void setIterPerStep(int ite);
 
-protected:
-    void init();
+    void release();
+    SimulatedAnnealingSolver(const SimulatedAnnealingSolver&);
+    SimulatedAnnealingSolver& operator=(const SimulatedAnnealingSolver&);
 
-private:
-    struct Impl;
+    struct Impl; friend struct Impl;
+protected:
     Impl* impl;
 };
+
+
 //! @} ml
 
 }
index 2ab0537..5652da2 100644 (file)
@@ -44,21 +44,37 @@ namespace cv { namespace ml {
 
 struct SimulatedAnnealingSolver::Impl
 {
+    int refcount;
+
+    const Ptr<SimulatedAnnealingSolverSystem> systemPtr;
+    SimulatedAnnealingSolverSystem& system;
     RNG rEnergy;
     double coolingRatio;
     double initialT;
     double finalT;
     int iterPerStep;
-    Impl()
+
+    Impl(const Ptr<SimulatedAnnealingSolverSystem>& s) :
+        refcount(1),
+        systemPtr(s),
+        system(*(s.get())),
+        rEnergy(12345)
     {
+        CV_Assert(!systemPtr.empty());
         initialT = 2;
         finalT = 0.1;
         coolingRatio = 0.95;
         iterPerStep = 100;
-        refcount = 1;
     }
-    int refcount;
-    ~Impl() { refcount--;CV_Assert(refcount==0); }
+
+    inline double energy() { return system.energy(); }
+    inline void changeState() { system.changeState(); }
+    inline void reverseState() { system.reverseState(); }
+
+    void addref() { CV_XADD(&refcount, 1); }
+    void release() { if (CV_XADD(&refcount, -1) == 1) delete this; }
+protected:
+    virtual ~Impl() { CV_Assert(refcount==0); }
 };
 
 struct AnnParams
@@ -71,7 +87,7 @@ struct AnnParams
         rpDW0 = 0.1; rpDWPlus = 1.2; rpDWMinus = 0.5;
         rpDWMin = FLT_EPSILON; rpDWMax = 50.;
         initialT=10;finalT=0.1,coolingRatio=0.95;itePerStep=10;
-
+        rEnergy = cv::RNG(12345);
     }
 
     TermCriteria termCrit;
@@ -90,6 +106,7 @@ struct AnnParams
     double finalT;
     double coolingRatio;
     int itePerStep;
+    RNG rEnergy;
 };
 
 template <typename T>
@@ -98,48 +115,60 @@ inline T inBounds(T val, T min_val, T max_val)
     return std::min(std::max(val, min_val), max_val);
 }
 
-SimulatedAnnealingSolver::~SimulatedAnnealingSolver()
+SimulatedAnnealingSolver::SimulatedAnnealingSolver(const Ptr<SimulatedAnnealingSolverSystem>& system)
+{
+    impl = new Impl(system);
+}
+
+SimulatedAnnealingSolver::SimulatedAnnealingSolver(const SimulatedAnnealingSolver& b)
 {
-    if (impl) delete impl;
+    if (b.impl) b.impl->addref();
+    release();
+    impl = b.impl;
 }
 
-void SimulatedAnnealingSolver::init()
+void SimulatedAnnealingSolver::release()
 {
-    impl = new SimulatedAnnealingSolver::Impl();
+    if (impl) { impl->release(); impl = NULL; }
 }
 
 void SimulatedAnnealingSolver::setIterPerStep(int ite)
 {
+    CV_Assert(impl);
     CV_Assert(ite>0);
     impl->iterPerStep = ite;
 }
 
 int SimulatedAnnealingSolver::run()
 {
+    CV_Assert(impl);
     CV_Assert(impl->initialT>impl->finalT);
     double Ti = impl->initialT;
-    double previousEnergy = energy();
+    double previousEnergy = impl->energy();
     int exchange = 0;
     while (Ti > impl->finalT)
     {
         for (int i = 0; i < impl->iterPerStep; i++)
         {
-            changedState();
-            double newEnergy = energy();
+            impl->changeState();
+            double newEnergy = impl->energy();
             if (newEnergy < previousEnergy)
             {
                 previousEnergy = newEnergy;
+                //??? exchange++;
             }
             else
             {
-                double r = impl->rEnergy.uniform(double(0.0), double(1.0));
-                if (r < exp(-(newEnergy - previousEnergy) / Ti))
+                double r = impl->rEnergy.uniform(0.0, 1.0);
+                if (r < std::exp(-(newEnergy - previousEnergy) / Ti))
                 {
                     previousEnergy = newEnergy;
                     exchange++;
                 }
                 else
-                    reverseChangedState();
+                {
+                    impl->reverseState();
+                }
             }
 
         }
@@ -149,33 +178,43 @@ int SimulatedAnnealingSolver::run()
     return exchange;
 }
 
+void SimulatedAnnealingSolver::setEnergyRNG(const RNG& rng)
+{
+    CV_Assert(impl);
+    impl->rEnergy = rng;
+}
+
 void SimulatedAnnealingSolver::setInitialTemperature(double x)
 {
+    CV_Assert(impl);
     CV_Assert(x>0);
     impl->initialT = x;
 }
 
 void SimulatedAnnealingSolver::setFinalTemperature(double x)
 {
+    CV_Assert(impl);
     CV_Assert(x>0);
     impl->finalT = x;
 }
 
 double SimulatedAnnealingSolver::getFinalTemperature()
 {
+    CV_Assert(impl);
     return impl->finalT;
 }
 
 void SimulatedAnnealingSolver::setCoolingRatio(double x)
 {
+    CV_Assert(impl);
     CV_Assert(x>0 && x<1);
     impl->coolingRatio = x;
 }
 
-class SimulatedAnnealingANN_MLP : public ml::SimulatedAnnealingSolver
+class SimulatedAnnealingANN_MLP : public SimulatedAnnealingSolverSystem
 {
-public:
-    ml::ANN_MLP *nn;
+protected:
+    ml::ANN_MLPnn;
     Ptr<ml::TrainData> data;
     int nbVariables;
     vector<double*> adrVariables;
@@ -183,13 +222,14 @@ public:
     RNG rIndex;
     double varTmp;
     int index;
-
-    SimulatedAnnealingANN_MLP(ml::ANN_MLP *x, Ptr<ml::TrainData> d) : nn(x), data(d)
+public:
+    SimulatedAnnealingANN_MLP(ml::ANN_MLP& x, const Ptr<ml::TrainData>& d) : nn(x), data(d)
     {
         initVarMap();
     }
-
-    void changedState()
+    ~SimulatedAnnealingANN_MLP() {}
+protected:
+    void changeState()
     {
         index = rIndex.uniform(0, nbVariables);
         double dv = rVar.uniform(-1.0, 1.0);
@@ -197,22 +237,22 @@ public:
         *adrVariables[index] = dv;
     }
 
-    void reverseChangedState()
+    void reverseState()
     {
         *adrVariables[index] = varTmp;
     }
 
-    double energy() { return nn->calcError(data, false, noArray()); }
+    double energy() const { return nn.calcError(data, false, noArray()); }
 
 protected:
     void initVarMap()
     {
-        Mat l = nn->getLayerSizes();
+        Mat l = nn.getLayerSizes();
         nbVariables = 0;
         adrVariables.clear();
         for (int i = 1; i < l.rows-1; i++)
         {
-            Mat w = nn->getWeights(i);
+            Mat w = nn.getWeights(i);
             for (int j = 0; j < w.rows; j++)
             {
                 for (int k = 0; k < w.cols; k++, nbVariables++)
@@ -296,6 +336,13 @@ void ANN_MLP::setAnnealItePerStep(int val)
     this_->setAnnealItePerStep(val);
 }
 
+void ANN_MLP::setAnnealEnergyRNG(const RNG& rng)
+{
+    ANN_MLP_ANNEAL* this_ = dynamic_cast<ANN_MLP_ANNEAL*>(this);
+    if (!this_)
+        CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL");
+    this_->setAnnealEnergyRNG(rng);
+}
 
 class ANN_MLPImpl : public ANN_MLP_ANNEAL
 {
@@ -323,6 +370,9 @@ public:
     CV_IMPL_PROPERTY(double, AnnealCoolingRatio, params.coolingRatio)
     CV_IMPL_PROPERTY(int, AnnealItePerStep, params.itePerStep)
 
+    //CV_IMPL_PROPERTY(RNG, AnnealEnergyRNG, params.rEnergy)
+    inline void setAnnealEnergyRNG(const RNG& val) { params.rEnergy = val; }
+
     void clear()
     {
         min_val = max_val = min_val1 = max_val1 = 0.;
@@ -1040,7 +1090,8 @@ public:
     }
     int train_anneal(const Ptr<TrainData>& trainData)
     {
-        SimulatedAnnealingANN_MLP t(this, trainData);
+        SimulatedAnnealingSolver t(Ptr<SimulatedAnnealingANN_MLP>(new SimulatedAnnealingANN_MLP(*this, trainData)));
+        t.setEnergyRNG(params.rEnergy);
         t.setFinalTemperature(params.finalT);
         t.setInitialTemperature(params.initialT);
         t.setCoolingRatio(params.coolingRatio);
@@ -1687,70 +1738,6 @@ Ptr<ANN_MLP> ANN_MLP::load(const String& filepath)
     return ann;
 }
 
-double ANN_MLP_ANNEAL::getAnnealInitialT() const
-{
-    const ANN_MLPImpl* this_ = dynamic_cast<const ANN_MLPImpl*>(this);
-    if (!this_)
-        CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL");
-    return this_->getAnnealInitialT();
-}
-
-void ANN_MLP_ANNEAL::setAnnealInitialT(double val)
-{
-    ANN_MLPImpl* this_ = dynamic_cast< ANN_MLPImpl*>(this);
-    if (!this_)
-        CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL");
-    this_->setAnnealInitialT(val);
-}
-
-double ANN_MLP_ANNEAL::getAnnealFinalT() const
-{
-    const ANN_MLPImpl* this_ = dynamic_cast<const ANN_MLPImpl*>(this);
-    if (!this_)
-        CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL");
-    return this_->getAnnealFinalT();
-}
-
-void ANN_MLP_ANNEAL::setAnnealFinalT(double val)
-{
-    ANN_MLPImpl* this_ = dynamic_cast<ANN_MLPImpl*>(this);
-    if (!this_)
-        CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL");
-    this_->setAnnealFinalT(val);
-}
-
-double ANN_MLP_ANNEAL::getAnnealCoolingRatio() const
-{
-    const ANN_MLPImpl* this_ = dynamic_cast<const ANN_MLPImpl*>(this);
-    if (!this_)
-        CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL");
-    return this_->getAnnealCoolingRatio();
-}
-
-void ANN_MLP_ANNEAL::setAnnealCoolingRatio(double val)
-{
-    ANN_MLPImpl* this_ = dynamic_cast< ANN_MLPImpl*>(this);
-    if (!this_)
-        CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL");
-    this_->setAnnealInitialT(val);
-}
-
-int ANN_MLP_ANNEAL::getAnnealItePerStep() const
-{
-    const ANN_MLPImpl* this_ = dynamic_cast<const ANN_MLPImpl*>(this);
-    if (!this_)
-        CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL");
-    return this_->getAnnealItePerStep();
-}
-
-void ANN_MLP_ANNEAL::setAnnealItePerStep(int val)
-{
-    ANN_MLPImpl* this_ = dynamic_cast<ANN_MLPImpl*>(this);
-    if (!this_)
-        CV_Error(Error::StsNotImplemented, "the class is not ANN_MLP_ANNEAL");
-    this_->setAnnealInitialT(val);
-}
-
 }}
 
 /* End of file. */
index a193606..8dc7a2d 100644 (file)
@@ -41,6 +41,8 @@
 
 #include "test_precomp.hpp"
 
+//#define GENERATE_TESTDATA
+
 using namespace cv;
 using namespace std;
 
@@ -248,7 +250,7 @@ TEST(ML_ANN, ActivationFunction)
 #endif
     }
 }
-//#define GENERATE_TESTDATA
+
 TEST(ML_ANN, Method)
 {
     String folder = string(cvtest::TS::ptr()->get_data_path());
@@ -275,6 +277,7 @@ TEST(ML_ANN, Method)
     methodName.push_back("_anneal");
 //    methodName.push_back("_backprop"); -----> NO BACKPROP TEST
 #ifdef GENERATE_TESTDATA
+    {
     Ptr<ml::ANN_MLP> xx = ml::ANN_MLP_ANNEAL::create();
     Mat_<int> layerSizesXX(1, 4);
     layerSizesXX(0, 0) = tdata->getNVars();
@@ -290,6 +293,7 @@ TEST(ML_ANN, Method)
     fs.open(dataname + "_init_weight.yml.gz", FileStorage::WRITE + FileStorage::BASE64);
     xx->write(fs);
     fs.release();
+    }
 #endif
     for (size_t i = 0; i < methodType.size(); i++)
     {
@@ -300,6 +304,7 @@ TEST(ML_ANN, Method)
         x->setTrainMethod(methodType[i]);
         if (methodType[i] == ml::ANN_MLP::ANNEAL)
         {
+            x->setAnnealEnergyRNG(RNG(CV_BIG_INT(0xffffffff)));
             x->setAnnealInitialT(12);
             x->setAnnealFinalT(0.15);
             x->setAnnealCoolingRatio(0.96);
index 138f61d..5a355b0 100644 (file)
@@ -1,68 +1,62 @@
 #include <opencv2/opencv.hpp>
 
-using namespace std;
 using namespace cv;
 
-void DrawTravelMap(Mat &img, vector<Point> &p, vector<int> &n);
-
-class TravelSalesman : public ml::SimulatedAnnealingSolver
+class TravelSalesman : public ml::SimulatedAnnealingSolverSystem
 {
 private :
-    vector<Point> &posCity;
-    vector<int> &next;
+    const std::vector<Point>& posCity;
+    std::vector<int>& next;
     RNG rng;
     int d0,d1,d2,d3;
 
 public:
-
-    TravelSalesman(vector<Point> &p,vector<int> &n):posCity(p),next(n)
+    TravelSalesman(std::vector<Point> &p, std::vector<int> &n) :
+        posCity(p), next(n)
     {
         rng = theRNG();
-    };
+    }
     /** Give energy value for  a state of system.*/
-    virtual double energy();
+    /*virtual*/ double energy() const;
     /** Function which change the state of system (random pertubation).*/
-    virtual void changedState();
+    /*virtual*/ void changeState();
     /** Function to reverse to the previous state.*/
-    virtual void reverseChangedState();
+    /*virtual*/ void reverseState();
 
 };
 
-void TravelSalesman::changedState()
+void TravelSalesman::changeState()
 {
     d0 = rng.uniform(0,static_cast<int>(posCity.size()));
     d1 = next[d0];
     d2 = next[d1];
     d3 = next[d2];
-    int d0Tmp = d0;
-    int d1Tmp = d1;
-    int d2Tmp = d2;
 
-    next[d0Tmp] = d2;
-    next[d2Tmp] = d1;
-    next[d1Tmp] = d3;
+    next[d0] = d2;
+    next[d2] = d1;
+    next[d1] = d3;
 }
 
 
-void TravelSalesman::reverseChangedState()
+void TravelSalesman::reverseState()
 {
     next[d0] = d1;
     next[d1] = d2;
     next[d2] = d3;
 }
 
-double TravelSalesman::energy()
+double TravelSalesman::energy() const
 {
-    double e=0;
+    double e = 0;
     for (size_t i = 0; i < next.size(); i++)
     {
-        e +=  norm(posCity[i]-posCity[next[i]]);
+        e += norm(posCity[i]-posCity[next[i]]);
     }
     return e;
 }
 
 
-void DrawTravelMap(Mat &img, vector<Point> &p, vector<int> &n)
+static void DrawTravelMap(Mat &img, std::vector<Point> &p, std::vector<int> &n)
 {
     for (size_t i = 0; i < n.size(); i++)
     {
@@ -74,12 +68,12 @@ int main(void)
 {
     int nbCity=40;
     Mat img(500,500,CV_8UC3,Scalar::all(0));
-    RNG &rng=theRNG();
+    RNG rng(123456);
     int radius=static_cast<int>(img.cols*0.45);
     Point center(img.cols/2,img.rows/2);
 
-    vector<Point> posCity(nbCity);
-    vector<int> next(nbCity);
+    std::vector<Point> posCity(nbCity);
+    std::vector<int> next(nbCity);
     for (size_t i = 0; i < posCity.size(); i++)
     {
         double theta = rng.uniform(0., 2 * CV_PI);
@@ -87,25 +81,33 @@ int main(void)
         posCity[i].y = static_cast<int>(radius*sin(theta)) + center.y;
         next[i]=(i+1)%nbCity;
     }
-    TravelSalesman ts(posCity,next);
+    Ptr<TravelSalesman> ts_system(new TravelSalesman(posCity, next));
+    ml::SimulatedAnnealingSolver ts(ts_system);
+
+    ts.setIterPerStep(10000*nbCity);
     ts.setCoolingRatio(0.99);
     ts.setInitialTemperature(100);
-    ts.setIterPerStep(10000*nbCity);
     ts.setFinalTemperature(100*0.97);
     DrawTravelMap(img,posCity,next);
     imshow("Map",img);
     waitKey(10);
-    for (int i = 0; i < 100; i++)
+    for (int i = 0, zeroChanges = 0; zeroChanges < 10; i++)
     {
-        ts.run();
+        int changesApplied = ts.run();
         img = Mat::zeros(img.size(),CV_8UC3);
         DrawTravelMap(img, posCity, next);
         imshow("Map", img);
-        waitKey(10);
+        int k = waitKey(10);
         double ti=ts.getFinalTemperature();
-        cout<<ti <<"  -> "<<ts.energy()<<"\n";
+        std::cout << "i=" << i << " changesApplied=" << changesApplied << " temp=" << ti << " result=" << ts_system->energy() << std::endl;
+        if (k == 27 || k == 'q' || k == 'Q')
+            return 0;
+        if (changesApplied == 0)
+            zeroChanges++;
         ts.setInitialTemperature(ti);
         ts.setFinalTemperature(ti*0.97);
     }
+    std::cout << "Done" << std::endl;
+    waitKey(0);
     return 0;
 }