From 00415cf61a2a082fd098a7eb5aaaf508db05c892 Mon Sep 17 00:00:00 2001
From: even <philippe.even@loria.fr>
Date: Mon, 8 Jul 2019 21:49:03 +0200
Subject: [PATCH] Code: Synthetic test image output and input

---
 Article/expe.tex                     |   2 +-
 Code/FBSD/BSTools/bsrandomtester.cpp | 528 ++++++++++++++++++++++++---
 Code/FBSD/BSTools/bsrandomtester.h   |  24 +-
 Code/FBSD/main.cpp                   |  22 +-
 Methode/answerToReview.tex           |  26 +-
 Methode/ctrl.tex                     |   6 +-
 6 files changed, 538 insertions(+), 70 deletions(-)

diff --git a/Article/expe.tex b/Article/expe.tex
index f18a024..c3f4ca7 100755
--- a/Article/expe.tex
+++ b/Article/expe.tex
@@ -15,7 +15,7 @@ thickness value output by the new detector.
 Moreover, we did not find any data base with ground truth including
 line thickness.
 Therefore, we proceed in two steps:
-(i) evaluation on synthetic images of the new concepts enhancements
+(i) evaluation on synthetic images of the new concepts enhancement
 on line orientation and thickness estimation;
 (ii) evaluation of more global performance of the proposed approach
 compared to other detectors.
diff --git a/Code/FBSD/BSTools/bsrandomtester.cpp b/Code/FBSD/BSTools/bsrandomtester.cpp
index b11ac91..f99e5ff 100755
--- a/Code/FBSD/BSTools/bsrandomtester.cpp
+++ b/Code/FBSD/BSTools/bsrandomtester.cpp
@@ -33,95 +33,439 @@ BSRandomTester::BSRandomTester ()
   dispEach = false;
   dispLast = false;
 
-  rp1 = new Pt2i[nbsegs];
-  rp2 = new Pt2i[nbsegs];
-  rdir = new Vr2i[nbsegs];
-  rw = new int[nbsegs];
+  nbIniPts = NULL;
+
+  c_trials = NULL;
+  c_det = NULL;
+  c_ldet = NULL;
+  c_unmatch = NULL;
+  c_undet = NULL;
+  c_true = NULL;
+  c_false = NULL;
+  c_redet = NULL;
+
+  m_precision = NULL;
+  m_recall = NULL;
+  m_fmeasure = NULL;
+  m_matched = NULL;
+  m_biased_width = NULL;
+  m_width = NULL;
+  m_wdiff = NULL;
+  m_abswdiff = NULL;
+  m_adiff = NULL;
+  m_absadiff = NULL;
+  m_long_absadiff = NULL;
+
+  rp1 = NULL;
+  rp2 = NULL;
+  rdir = NULL;
+  rw = NULL;
+
+  gMap = NULL;
+  detectors = new BSDetector[nbdets];
+  for (int i = 0; i < nbdets; i++)
+  {
+    detectors[i].setAssignedThickness (smaxwidth + swmargin);
+    if (! detectors[i].isFinalSizeTestOn ())
+      detectors[i].switchFinalSizeTest ();
+  }
+  detectors[0].setStaticDetector (true);
+  detectors[1].setStaticDetector (false);
+  names = new QString[nbdets];
+  names[0] = "old";
+  names[1] = "new";
+
+  image = QImage (width, height, QImage::Format_RGB32);
   tofind_map = new bool[isize];
   hit_map = new int[isize];
   stilltofind_map = new bool[isize];
   found_map = new bool[isize];
   foundin_map = new bool[isize];
   foundout_map = new bool[isize];
+  srand (time (NULL));
+}
+
+
+BSRandomTester::~BSRandomTester ()
+{
+  if (gMap != NULL) delete gMap;
+  if (names != NULL) delete [] names;
+  if (detectors != NULL) delete [] detectors;
+  if (m_long_absadiff != NULL) delete [] m_long_absadiff;
+  if (m_absadiff != NULL) delete [] m_absadiff;
+  if (m_adiff != NULL) delete [] m_adiff;
+  if (m_abswdiff != NULL) delete [] m_abswdiff;
+  if (m_wdiff != NULL) delete [] m_wdiff;
+  if (m_width != NULL) delete [] m_width;
+  if (m_biased_width != NULL) delete [] m_biased_width;
+  if (m_matched != NULL) delete [] m_matched;
+  if (m_fmeasure != NULL) delete [] m_fmeasure;
+  if (m_recall != NULL) delete [] m_recall;
+  if (m_precision != NULL) delete [] m_precision;
+  if (c_redet != NULL) delete [] c_redet;
+  if (c_false != NULL) delete [] c_false;
+  if (c_true != NULL) delete [] c_true;
+  if (c_undet != NULL) delete [] c_undet;
+  if (c_unmatch != NULL) delete [] c_unmatch;
+  if (c_ldet != NULL) delete [] c_ldet;
+  if (c_det != NULL) delete [] c_det;
+  if (c_trials != NULL) delete [] c_trials;
+  if (nbIniPts != NULL) delete [] nbIniPts;
+  if (foundout_map != NULL) delete [] foundout_map;
+  if (foundin_map != NULL) delete [] foundin_map;
+  if (found_map != NULL) delete [] found_map;
+  if (stilltofind_map != NULL) delete [] stilltofind_map;
+  if (hit_map != NULL) delete [] hit_map;
+  if (tofind_map != NULL) delete [] tofind_map;
+  if (rw != NULL) delete [] rw;
+  if (rdir != NULL) delete [] rdir;
+  if (rp2 != NULL) delete [] rp2;
+  if (rp1 != NULL) delete [] rp1;
+}
+
 
+void BSRandomTester::initRuns ()
+{
+  if (nbIniPts != NULL) delete nbIniPts;
   nbIniPts = new int[nbruns];
   int nbt = nbdets * nbruns;
 
+  if (c_trials != NULL) delete c_trials;
   c_trials = new int[nbt];
+  if (c_det != NULL) delete c_det;
   c_det = new int[nbt];
+  if (c_ldet != NULL) delete c_ldet;
   c_ldet = new int[nbt];
+  if (c_unmatch != NULL) delete c_unmatch;
   c_unmatch = new int[nbt];
+  if (c_undet != NULL) delete c_undet;
   c_undet = new int[nbt];
+  if (c_true != NULL) delete c_true;
   c_true = new int[nbt];
+  if (c_false != NULL) delete c_false;
   c_false = new int[nbt];
+  if (c_redet != NULL) delete c_redet;
   c_redet = new int[nbt];
 
+  if (m_precision != NULL) delete m_precision;
   m_precision = new double[nbt];
+  if (m_recall != NULL) delete m_recall;
   m_recall = new double[nbt];
+  if (m_fmeasure != NULL) delete m_fmeasure;
   m_fmeasure = new double[nbt];
+  if (m_matched != NULL) delete m_matched;
   m_matched = new double[nbt];
+  if (m_biased_width != NULL) delete m_biased_width;
   m_biased_width = new double[nbt];
+  if (m_width != NULL) delete m_width;
   m_width = new double[nbt];
+  if (m_wdiff != NULL) delete m_wdiff;
   m_wdiff = new double[nbt];
+  if (m_abswdiff != NULL) delete m_abswdiff;
   m_abswdiff = new double[nbt];
+  if (m_adiff != NULL) delete m_adiff;
   m_adiff = new double[nbt];
+  if (m_absadiff != NULL) delete m_absadiff;
   m_absadiff = new double[nbt];
+  if (m_long_absadiff != NULL) delete m_long_absadiff;
   m_long_absadiff = new double[nbt];
+}
 
-  gMap = NULL;
-  detectors = new BSDetector[nbdets];
-  for (int i = 0; i < nbdets; i++)
-  {
-    detectors[i].setAssignedThickness (smaxwidth + swmargin);
-    if (! detectors[i].isFinalSizeTestOn ())
-      detectors[i].switchFinalSizeTest ();
-  }
-  detectors[0].setStaticDetector (true);
-  detectors[1].setStaticDetector (false);
-  names = new QString[nbdets];
-  names[0] = "old";
-  names[1] = "new";
 
-  image = QImage (width, height, QImage::Format_RGB32);
-  srand (time (NULL));
+void BSRandomTester::initSegs ()
+{
+  if (rp1 != NULL) delete rp1;
+  rp1 = new Pt2i[nbsegs];
+  if (rp2 != NULL) delete rp2;
+  rp2 = new Pt2i[nbsegs];
+  if (rdir != NULL) delete rdir;
+  rdir = new Vr2i[nbsegs];
+  if (rw != NULL) delete rw;
+  rw = new int[nbsegs];
 }
 
 
-BSRandomTester::~BSRandomTester ()
+void BSRandomTester::singleTest (bool in)
 {
+  // List of detected segments match to each input segment
+  nbruns = 1;
+  initRuns ();
+
+  // Generates an image and provide it to the detectors
+  if (in)
+  {
+    if (! reloadImage ()) return;
+    cout << "Testing ..." << endl;
+  }
+  else
+  {
+    cout << "Testing ..." << endl;
+    initSegs ();
+    generateImage ();
+    image.save ("randimage.png");
+    ofstream outf ("randin.txt", ios::out);
+    outf << nbsegs << endl;
+    for (int i = 0; i < nbsegs; i++)
+    {
+      outf << rp1[i].x () << " " << rp1[i].y () << endl;
+      outf << rp2[i].x () << " " << rp2[i].y () << endl;
+      outf << rw[i] << endl;
+    }
+    outf.close ();
+  }
+
+  vector<BlurredSegment *> rbs[nbsegs];
   if (gMap != NULL) delete gMap;
-  delete [] names;
-  delete [] detectors;
-  delete [] m_long_absadiff;
-  delete [] m_absadiff;
-  delete [] m_adiff;
-  delete [] m_abswdiff;
-  delete [] m_wdiff;
-  delete [] m_width;
-  delete [] m_biased_width;
-  delete [] m_matched;
-  delete [] m_fmeasure;
-  delete [] m_recall;
-  delete [] m_precision;
-  delete [] c_redet;
-  delete [] c_false;
-  delete [] c_true;
-  delete [] c_undet;
-  delete [] c_unmatch;
-  delete [] c_ldet;
-  delete [] c_det;
-  delete [] c_trials;
-  delete [] nbIniPts;
-  delete [] foundout_map;
-  delete [] foundin_map;
-  delete [] found_map;
-  delete [] stilltofind_map;
-  delete [] hit_map;
-  delete [] tofind_map;
-  delete [] rw;
-  delete [] rdir;
-  delete [] rp2;
-  delete [] rp1;
+  gMap = new VMap (width, height, getBitmap (image), VMap::TYPE_SOBEL_5X5);
+  gMap->incGradientThreshold (50 - gMap->getGradientThreshold ());
+  for (int det = 0; det < nbdets; det ++)
+  {
+    detectors[det].setGradientMap (gMap);
+    if (detectors[det].isSingleEdgeModeOn ())
+      detectors[det].switchSingleOrDoubleEdge ();
+  }
+
+  for (int det = 0; det < nbdets; det ++)
+  {
+    // Detects the segments
+    detectors[det].detectAll ();
+
+    nbIniPts[0] = 0;
+    for (int i = 0; i < isize; i++)
+    {
+      stilltofind_map[i] = tofind_map[i];
+      found_map[i] = false;
+      foundin_map[i] = false;
+      foundout_map[i] = false;
+      if (tofind_map[i]) nbIniPts[0] ++;
+      hit_map[i] = 0;
+    }
+
+    for (int i = 0; i < nbsegs; i++) rbs[i].clear ();
+    c_unmatch[det] = 0;
+    int nbdssnul = 0;
+    c_ldet[det] = 0;
+    double nomatchlength = 0.;
+    vector<BlurredSegment *> bss = detectors[det].getBlurredSegments ();
+    vector<BlurredSegment *>::iterator bsit = bss.begin ();
+    while (bsit != bss.end ())
+    {
+      DigitalStraightSegment *dss = (*bsit)->getSegment ();
+      if (dss != NULL)
+      {
+        // Fills in occupancy maps
+        if (unbiasOn) dss = dss->erosion (biasVal.numerator (),
+                                          biasVal.denominator ());
+        vector<Pt2i> dsspts;
+        dss->getPoints (dsspts);
+        vector<Pt2i>::iterator dssit = dsspts.begin ();
+        while (dssit != dsspts.end ())
+        {
+          Pt2i dsspt = *dssit++;
+          if (dsspt.x () >= 0 && dsspt.x () < width
+              && dsspt.y () >= 0 && dsspt.y () < height)
+          {
+            stilltofind_map[dsspt.y () * width + dsspt.x ()] = false;
+            found_map[dsspt.y () * width + dsspt.x ()] = true;
+            if (tofind_map[dsspt.y () * width + dsspt.x ()])
+            {
+              hit_map[dsspt.y () * width + dsspt.x ()] ++;
+              foundin_map[dsspt.y () * width + dsspt.x ()] = true;
+            }
+            else
+            {
+              hit_map[dsspt.y () * width + dsspt.x ()] --;
+              foundout_map[dsspt.y () * width + dsspt.x ()] = true;
+            }
+          }
+        }
+
+        // Matches the detected segment with the nearest input segment
+        Vr2i dssdir = dss->supportVector ();
+        Pt2i bsc = (*bsit)->getMiddle ();
+        double bsl2 = (*bsit)->getSquarredLength ();
+        if (bsl2 > longEdgeThreshold) c_ldet[det] ++;
+        double score[nbsegs];
+        double minscore = 0.;
+        int bestfit = -1;
+        for (int si = 0; si < nbsegs; si++)
+        {
+          double denom = rdir[si].norm2 () * dssdir.norm2 ();
+          score[si] = rdir[si].squaredScalarProduct (dssdir) / denom;
+          Vr2i bsca = rp1[si].vectorTo (bsc);
+          Vr2i bscb = bsc.vectorTo (rp2[si]);
+          Vr2i *bsac = (rp1[si].chessboard (bsc) < sminlength / 2 ?
+                        &bscb : &bsca);
+          denom = rdir[si].norm2 () * bsac->norm2 ();
+          score[si] *= rdir[si].squaredScalarProduct (*bsac) / denom;
+          if (rdir[si].scalarProduct (bsca) < 0) score[si] = 0.;
+          if (rdir[si].scalarProduct (bscb) < 0) score[si] = 0.;
+          if (minscore < score[si])
+          {
+            minscore = score[si];
+            bestfit = si;
+          }
+        }
+        if (minscore > 0.7) rbs[bestfit].push_back (*bsit);
+        else
+        {
+          c_unmatch[det] ++;
+          nomatchlength += sqrt (bsl2);
+        }
+        if (unbiasOn) delete dss;
+      }
+      else nbdssnul ++;
+      bsit ++;
+    }
+
+    c_trials[det] = detectors[det].countOfTrials ();
+    c_det[det] = (int) (detectors[det].getBlurredSegments().size ());
+    if (dispLast) createMap (names[det]);
+    c_undet[det] = 0;
+    c_true[det] = 0;
+    c_false[det] = 0;
+    for (int i = 0; i < width * height; i++)
+    {
+      if (stilltofind_map[i]) c_undet[det] ++;
+      if (foundin_map[i]) c_true[det] ++;
+      if (foundout_map[i]) c_false[det] ++;
+    }
+    c_redet[det] = 0;
+    for (int i = 0; i < width * height; i++)
+      if (hit_map[i] > 1) c_redet[det] += (hit_map[i] - 1);
+    m_precision[det] = nbIniPts[0] - c_undet[det];
+    m_recall[det] = m_precision[det] / nbIniPts[0];
+    if (m_precision[det] + c_false[det] != 0)
+    {
+      m_precision[det] = m_precision[det] / (m_precision[det] + c_false[det]);
+      m_fmeasure[det] = 2 * m_precision[det] * m_recall[det]
+                        / (m_precision[det] + m_recall[det]);
+    }
+    else
+    {
+      m_precision[det] = 0.;
+      m_fmeasure[det] = 0.;
+    }
+
+    m_biased_width[det] = 0.;
+    m_width[det] = 0.;
+    m_wdiff[det] = 0.;
+    m_abswdiff[det] = 0.;
+    m_adiff[det] = 0.;
+    m_absadiff[det] = 0.;
+    m_long_absadiff[det] = 0.;
+    m_matched[det] = 0;
+    for (int si = 0; si < nbsegs; si ++)
+    {
+      // Compares input and detected segments
+      if (! rbs[si].empty ()) m_matched[det] ++;
+      vector<BlurredSegment *>::iterator sit = rbs[si].begin ();
+      while (sit != rbs[si].end ())
+      {
+        double bsl2 = (*sit)->getSquarredLength ();
+        DigitalStraightSegment *mydss = (*sit)->getSegment ();
+        m_biased_width[det] += (mydss->width () / (double) (mydss->period ()))
+                               * sqrt (bsl2) / sqrt (rdir[si].norm2 ());
+        if (unbiasOn) mydss = mydss->erosion (biasVal.numerator (),
+                                              biasVal.denominator ());
+        m_width[det] += (mydss->width () / (double) (mydss->period ()))
+                         * sqrt (bsl2) / sqrt (rdir[si].norm2 ());
+        double wd = (mydss->width () / (double) (mydss->period ()) - rw[si])
+                    * sqrt (bsl2) / sqrt (rdir[si].norm2 ());
+        m_wdiff[det] += wd;
+        if (wd < 0) wd = -wd;
+        m_abswdiff[det] += wd;
+        Vr2i mydir = mydss->supportVector ();
+        double ang = rdir[si].scalarProduct (mydir);
+        bool onleft = rdir[si].leftside (mydir);
+        if (ang < 0.)
+        {
+          ang = - ang;
+          onleft = - onleft;
+        }
+        double den = sqrt (rdir[si].norm2 ()) * sqrt (mydir.norm2 ());
+        if (den > ang)
+        {
+          ang = acos (ang / den) * 180 / M_PI;
+          ang *= sqrt (bsl2) / sqrt (rdir[si].norm2 ());
+          m_absadiff[det] += ang;
+          m_adiff[det] += (onleft ? ang : -ang);
+          if (bsl2 > longEdgeThreshold) m_long_absadiff[det] += ang;
+        }
+        if (unbiasOn) delete mydss;
+        sit ++;
+      }
+    }
+  }
+
+  for (int det = 0; det < nbdets; det ++)
+  {
+    cout << "  RESULTS FOR THE DETECTOR " << names[det].toStdString () << endl;
+    cout << c_trials[det] << " segments searches (local min)" << endl;
+    cout << c_det[det] << " provided segments" << endl;
+    cout << c_ldet[det] << " provided long segments" << endl;
+    cout << c_unmatch[det] << " undetected segments" << endl;
+
+    cout << ((100. * c_redet[det]) / nbIniPts[0])
+         << " % of points found more than once (redetections)" << endl;
+    cout << ((100 * c_false[det]) / nbIniPts[0])
+         << " % false points produced" << endl;
+
+    cout << "Precision : " << m_precision[det] << endl;
+    cout << "Recall : " << m_recall[det] << endl;
+    cout << "F-measure : " << m_fmeasure[det] << endl;
+
+    cout << "Biased width : " << (m_biased_width[det] / (double) m_matched[det])
+         << " per matched segment" << endl;
+    cout << "Width : " << (m_width[det] / (double) m_matched[det])
+         << " per matched segment" << endl;
+    cout << "Width difference : " << (m_wdiff[det] / (double) m_matched[det])
+         << " per matched segment" << endl;
+    cout << "Absolute width difference : "
+         << (m_abswdiff[det] / (double) m_matched[det])
+         << " per matched segment" << endl;
+    cout << "Angle difference : " << (m_adiff[det] / (double) m_matched[det])
+         << " degrees per matched segment" << endl;
+    cout << "Absolute angle difference : "
+         << (m_absadiff[det] / (double) m_matched[det])
+         << " per matched segment" << endl;
+    cout << "Absolute long edge angle difference : "
+         << (m_long_absadiff[det] / (double) m_matched[det])
+         << " per matched segment" << endl;
+
+    if (! in)
+    {
+      ofstream outf (det == 0 ? "randout_old.txt" : "randout_new.txt",
+                     ios::out);
+      outf << c_trials[det] << " segment searches (local min)" << endl;
+      outf << c_det[det] << " provided segments" << endl;
+      outf << c_ldet[det] << " provided long segments" << endl;
+      outf << c_unmatch[det] << " undetected segments" << endl;
+      outf << (c_redet[det] / (double) nbIniPts[0])
+           << " % of points found more than once (redetections)" << endl;
+      outf << (c_false[det] / (double) nbIniPts[0])
+           << " % of false points produced" << endl;
+      outf << m_precision[det] << " : precision" << endl;
+      outf << m_recall[det] << " : recall" << endl;
+      outf << m_fmeasure[det] << " : F-measure" << endl;
+      outf << (m_biased_width[det] / m_matched[det])
+           << " : Biased width / matched segment" << endl;
+      outf << (m_width[det] / m_matched[det])
+           << " : Width / matched segment" << endl;
+      outf << (m_wdiff[det] / m_matched[det])
+           << " : Width difference / matched segment" << endl;
+      outf << (m_abswdiff[det] / m_matched[det])
+           << " : Absolute width difference / matched segment" << endl;
+      outf << (m_adiff[det] / m_matched[det])
+           << " : Angle difference / matched segment" << endl;
+      outf << (m_absadiff[det] / m_matched[det])
+           << " : Absolute angle difference / matched segment" << endl;
+      outf << (m_long_absadiff[det] / m_matched[det])
+           << " : Absolute long edge angle difference / matched segment"
+           << endl;
+      outf.close ();
+    }
+  }
 }
 
 
@@ -129,6 +473,8 @@ void BSRandomTester::randomTest ()
 {
   // List of detected segments match to each input segment
   vector<BlurredSegment *> rbs[nbsegs];
+  initRuns ();
+  initSegs ();
 
   cout << "Testing ..." << endl;
   for (int run = 0; run < nbruns; run ++)
@@ -136,6 +482,19 @@ void BSRandomTester::randomTest ()
     // Generates an image and provide it to the detectors
     if (dispEach) cout << "Generating new segments" << endl;
     generateImage ();
+    if (nbruns == 1)
+    {
+      image.save ("randimage.png");
+      ofstream outf ("randin.txt", ios::out);
+      outf << nbsegs << endl;
+      for (int i = 0; i < nbsegs; i++)
+      {
+        outf << rp1[i].x () << " " << rp1[i].y () << endl;
+        outf << rp2[i].x () << " " << rp2[i].y () << endl;
+        outf << rw[i] << endl;
+      }
+      outf.close ();
+    }
     if (gMap != NULL) delete gMap;
     gMap = new VMap (width, height, getBitmap (image), VMap::TYPE_SOBEL_5X5);
     gMap->incGradientThreshold (50 - gMap->getGradientThreshold ());
@@ -607,6 +966,39 @@ void BSRandomTester::randomTest ()
     sdev = sqrt (sdev / (nbruns - 1));
     cout << "Absolute long edge angle difference : " << mean << " (" << sdev
          << ") per matched segment" << endl;
+
+    if (nbruns == 1)
+    {
+      ofstream outf (det == 0 ? "randout_old.txt" : "randout_new.txt",
+                     ios::out);
+      outf << c_trials[det] << " segment searches (local min)" << endl;
+      outf << c_det[det] << " provided segments" << endl;
+      outf << c_ldet[det] << " provided long segments" << endl;
+      outf << c_unmatch[det] << " undetected segments" << endl;
+      outf << (c_redet[det] / (double) nbIniPts[0])
+           << " % of points found more than once (redetections)" << endl;
+      outf << (c_false[det] / (double) nbIniPts[0])
+           << " % of false points produced" << endl;
+      outf << m_precision[det] << " : precision" << endl;
+      outf << m_recall[det] << " : recall" << endl;
+      outf << m_fmeasure[det] << " : F-measure" << endl;
+      outf << (m_biased_width[det] / m_matched[det])
+           << " : Biased width / matched segment" << endl;
+      outf << (m_width[det] / m_matched[det])
+           << " : Width / matched segment" << endl;
+      outf << (m_wdiff[det] / m_matched[det])
+           << " : Width difference / matched segment" << endl;
+      outf << (m_abswdiff[det] / m_matched[det])
+           << " : Absolute width difference / matched segment" << endl;
+      outf << (m_adiff[det] / m_matched[det])
+           << " : Angle difference / matched segment" << endl;
+      outf << (m_absadiff[det] / m_matched[det])
+           << " : Absolute angle difference / matched segment" << endl;
+      outf << (m_long_absadiff[det] / m_matched[det])
+           << " : Absolute long edge angle difference / matched segment"
+           << endl;
+      outf.close ();
+    }
   }
 }
 
@@ -686,6 +1078,38 @@ void BSRandomTester::generateImage ()
 }
 
 
+bool BSRandomTester::reloadImage ()
+{
+  image.load ("randimage.png");
+  if (image.width () != width || image.height () != height)
+  {
+    cout << "Wrong random image size" << endl;
+    return false;
+  }
+  ifstream input ("randin.txt", ios::in);
+  int x = 0, y = 0;
+  input >> nbsegs;
+  initSegs ();
+  for (int i = 0; i < nbsegs; i++)
+  {
+    input >> x;
+    input >> y;
+    rp1[i] = Pt2i (x, y);
+    input >> x;
+    input >> y;
+    rp2[i] = Pt2i (x, y);
+    rdir[i] = rp1[i].vectorTo (rp2[i]);
+    input >> rw[i];
+  }
+
+  for (int j = 0; j < height; j++)
+    for (int i = 0; i < width; i++)
+      tofind_map[j * width + i] =
+        QColor (image.pixel (i, height - 1 - j)).value () < 10;
+  return true;
+}
+
+
 void BSRandomTester::createMap (QString name)
 {
   QImage mymap = QImage (width, height, QImage::Format_RGB32);
diff --git a/Code/FBSD/BSTools/bsrandomtester.h b/Code/FBSD/BSTools/bsrandomtester.h
index 773b4b7..e6557d1 100755
--- a/Code/FBSD/BSTools/bsrandomtester.h
+++ b/Code/FBSD/BSTools/bsrandomtester.h
@@ -29,7 +29,13 @@ public:
   ~BSRandomTester ();
 
   /**
-   * \brief Runs a random test.
+   * \brief Runs or replays a single random test.
+   * @param in Replays if set to true, otherwise runs a new test.
+   */
+  void singleTest (bool in);
+
+  /**
+   * \brief Runs a complete random test.
    */
   void randomTest ();
 
@@ -150,11 +156,27 @@ private:
   double *m_long_absadiff;
 
 
+  /**
+   * \brief Initializes local data associated to the count of runs.
+   */
+  void initRuns ();
+
+  /**
+   * \brief Initializes local data associated to the count of random segments.
+   */
+  void initSegs ();
+
   /**
    * \brief Generates a new random image of segments.
    */
   void generateImage ();
 
+  /**
+   * \brief Loads a randomly generated image of segments.
+   * Returns the loading success condition.
+   */
+  bool reloadImage ();
+
   /**
    * \brief Builds and returns the tested maps.
    */
diff --git a/Code/FBSD/main.cpp b/Code/FBSD/main.cpp
index 29b38fa..5bececd 100755
--- a/Code/FBSD/main.cpp
+++ b/Code/FBSD/main.cpp
@@ -11,6 +11,7 @@ int main (int argc, char *argv[])
   int val = 0;
   int imageName = 0;
   bool random = false, testing = false;
+  bool randout = false, randin = false;  // DEV
   bool xt = false, yt = false;    // DEV
   bool out = false;
   QApplication app (argc, argv);
@@ -33,6 +34,18 @@ int main (int argc, char *argv[])
     if (string(argv[i]).at(0) == '-')
     {
       if (string(argv[i]) == string ("-random")) random = true;
+// DEV IN
+      else if (string(argv[i]) == string ("-randout"))
+      {
+        random = true;
+        randout = true;
+      }
+      else if (string(argv[i]) == string ("-randin"))
+      {
+        random = true;
+        randin = true;
+      }
+// DEV OUT
       else if (string(argv[i]) == string ("-test")) testing = true;
 // DEV IN
       else if (string(argv[i]) == string ("-own"))
@@ -100,11 +113,16 @@ int main (int argc, char *argv[])
   if (random)
   {
     BSRandomTester *tester = new BSRandomTester ();
-    tester->randomTest ();
+// DEV IN
+    if (randout) tester->singleTest (false);
+    else if (randin) tester->singleTest (true);
+    else
+// DEV OUT
+      tester->randomTest ();
     delete tester;
     return (EXIT_SUCCESS);
   }
-  else if (out)
+  if (out)
   {
     QImage im;
     if (imageName != 0) im.load (argv[imageName]);
diff --git a/Methode/answerToReview.tex b/Methode/answerToReview.tex
index 94f5b32..dc821fa 100755
--- a/Methode/answerToReview.tex
+++ b/Methode/answerToReview.tex
@@ -34,7 +34,7 @@ We would like to thank the editors and reviewers for their work and
 for their constructive comments, questions and suggestions.
 Because the paper already reaches the 10 pages limit, and in order to
 avoid removal of possibly valuable contents for paper understanding,
-additional data are added to the github, that is referenced in the paper
+complementary data are added to the github, that is referenced in the paper
 ({\tt https://github.com/evenp/FBSD}).
 A detailed list of the changes is given below with also some specific
 answers to raised questions.
@@ -73,7 +73,7 @@ encourages reproducibility.
 \item Writing quality of the paper is good.
 \end{itemize}
 \begin{answer}
-Thanks.
+Thanks for positive remarks.
 \end{answer}
 
 \item {\bf 3. Weaknesses. Consider significance of key ideas, experiments,
@@ -90,7 +90,7 @@ Recognition (pp. 2031-2039).]), but mainly with three literature works,
 latest of which was published in 2015. \\
 
 \begin{answer}
-Thanks for pointing this out. We have added this reference to the paper.
+Thanks for pointing this out. We have added this reference in the paper.
 However, we prefer let the evaluations to be completed in an extended journal
 version for several reasons:
 (i) the source code provided by the authors is written in Matlab, that
@@ -99,7 +99,7 @@ language);
 (ii) this method presents some important ranking level parameters which
 could largely influence achieved results,
 and it should require more time to ensure fair comparisons. \\
-We briefly mention it at the beginning of section 4.
+We briefly mention these points in the paper at the beginning of section 4.
 \end{answer}
 
 Some notes are as belows:
@@ -125,13 +125,13 @@ that were used in the experiments and the performance of both versions of
 the method obtained on them ?
 \begin{answer}
 Due to page limitations (the organizers rather suggested us to add
-complementary materials in a web page), we could not add any figure nor
-respective performance result in the paper. However, a couple of examples
-of synthesized images is already available in the mentioned github, and
-we have completed the table with associated results. In accordance to the
-measured standard deviations obtained on the whole set of 1000 randomly
-generated images, large variations can be observed in such results on
-individual images.
+complementary materials in a referenced web page), we could not add any
+figure nor respective performance result in the paper. However, a couple
+of examples of synthesized images is already available in the mentioned
+github, and we have completed the table with associated results.
+In accordance to the measured standard deviations obtained on the whole
+set of 1000 randomly generated images, large variations can be observed
+in such results on individual images.
 \end{answer}
 
 \item What is understood from the paper is the performance results presented
@@ -169,7 +169,7 @@ Experiments on synthetic images aim at assessing the improved performance
 beween formal and actual versions of the detector, including the thickness
 value estimation. The synthesized images are generated with a thickness
 varying from 2 up to 5 pixels.
-Therefore the initial assigned thickness is set to a greater value : 7. \\
+Therefore the initial assigned thickness is set to a greater value: 7. \\
 The other detectors aim at providing thin lines and may reject too
 scattered image lines. To adapt to this behavior, we retrict the detection
 to thin lines using the initial assigned thickness to 3 pixels. \\
@@ -189,7 +189,7 @@ We have added all the required informations in the github
 table (T, N and L values were already available, along with mean thickness W).
 We notice that achieved values have less meaning here, because as explained
 in the paper, the lines detected by the former method are more likely to
-incorporate spurious points, that artificially grow the width and length
+incorporate spurious points, that artificially grows the width and length
 values.
 \end{answer}
 
diff --git a/Methode/ctrl.tex b/Methode/ctrl.tex
index e7891b8..86bcd88 100755
--- a/Methode/ctrl.tex
+++ b/Methode/ctrl.tex
@@ -116,7 +116,11 @@ $< \wedge > \vee$ && D\'ecalage de l'observation \\
 \multicolumn{3}{|l|}{Param\`etres d'ex\'ecution :} \\
 {\tt -out} && Sort les lignes d'une d\'etection auto sur l'image fournie
 dans {\tt naivelines.txt}. \\
-{\tt -random} && Lance un test de perfomance sur images synth\'etiques. \\
+{\tt -random} && Lance un test de perfomance sur 1000 images synth\'etiques. \\
+{\tt -randout}
+&& Lance un test de perfomance sur 1 image synth\'etique avec sauvegarde. \\
+{\tt -randin}
+&& Rejoue un test de perfomance sur 1 image synth\'etique sauvegard\'ee. \\
 \hline
 \end{tabular}
 
-- 
GitLab