diff --git a/Code/Seg/BSTools/bsdetectionwidget.cpp b/Code/Seg/BSTools/bsdetectionwidget.cpp
index c1af1a64d497a5265eb076fccc673f4674ca37b6..290d72a33b62da207a6eefc8d5f00b330e1f4a64 100755
--- a/Code/Seg/BSTools/bsdetectionwidget.cpp
+++ b/Code/Seg/BSTools/bsdetectionwidget.cpp
@@ -439,6 +439,13 @@ void BSDetectionWidget::keyPressEvent (QKeyEvent *event)
       extract (true);
       break;
 
+    case Qt::Key_J :
+      detector.switchPreliminary ();
+      cout << "Preliminary detection "
+           << (detector.isPreliminary () ? "on" : "off") << endl;
+      extract (true);
+      break;
+
     case Qt::Key_1 :
       switchPixelAnalyzer ();
       break;
@@ -711,6 +718,10 @@ void BSDetectionWidget::displayExtractionResult ()
     cout << "Extraction : undetermined." << endl;
   else if (res == BSDetector::RESULT_OK)
     cout << "Extraction : OK." << endl;
+  else if (res == BSDetector::RESULT_PRELIM_NO_DETECTION)
+    cout << "Extraction : no preliminary detection (bs0 == NULL)." << endl;
+  else if (res == BSDetector::RESULT_PRELIM_TOO_FEW)
+    cout << "Extraction : two few points at preliminary detection." << endl;
   else if (res == BSDetector::RESULT_INITIAL_NO_DETECTION)
     cout << "Extraction : no initial detection (bsini == NULL)." << endl;
   else if (res == BSDetector::RESULT_INITIAL_TOO_FEW)
@@ -759,26 +770,29 @@ void BSDetectionWidget::alternateTest ()
 
 void BSDetectionWidget::performanceTest ()
 {
-/*
-  if (p1.equals (p2))
+  if (detector.isMultiSelection ())
   {
-    cout << "Stroke undefined" << endl;
-    return;
+    cout << "Automatic extraction test" << endl;
+    clock_t start = clock ();
+    for (int i = 0; i < 100; i++) detector.detectAll ();
+    double diff = (clock () - start) / (double) CLOCKS_PER_SEC;
+    cout << "Test run : " << diff << endl;
+    extract (true);
+  }
+  else
+  {
+    if (p1.equals (p2))
+    {
+      cout << "Stroke undefined" << endl;
+      return;
+    }
+    cout << "Run test" << endl;
+    clock_t start = clock ();
+    for (int i = 0; i < 1000; i++) detector.detect (p1, p2);
+    double diff = (clock () - start) / (double) CLOCKS_PER_SEC;
+    cout << "Test run : " << diff << endl;
+    extract (true);
   }
-  cout << "Run test" << endl;
-  clock_t start = clock ();
-  for (int i = 0; i < 1000; i++) detector.detect (p1, p2);
-  double diff = (clock () - start) / (double) CLOCKS_PER_SEC;
-  cout << "Test run : " << diff << endl;
-  extract (true);
-*/
-
-  cout << "Complete extractions test" << endl;
-  clock_t start = clock ();
-  for (int i = 0; i < 100; i++) detector.detectAll ();
-  double diff = (clock () - start) / (double) CLOCKS_PER_SEC;
-  cout << "Test run : " << diff << endl;
-  extract (true);
 }
 
 
@@ -791,8 +805,8 @@ void BSDetectionWidget::localTest ()
   p2 = Pt2i (232, 152);
 */
 
-  p1 = Pt2i (298, 199);
-  p2 = Pt2i (279, 173);
+  p1 = Pt2i (157, 125);
+  p2 = Pt2i (165, 88);
 
   extract (true);
   cout << "Test run" << endl;
diff --git a/Code/Seg/BlurredSegment/bsdetector.cpp b/Code/Seg/BlurredSegment/bsdetector.cpp
index efa1ae6f700667e92f449329dc471706310af7da..0b742192073aa1a5b1cfd462e46c2c13bdf09cb4 100755
--- a/Code/Seg/BlurredSegment/bsdetector.cpp
+++ b/Code/Seg/BlurredSegment/bsdetector.cpp
@@ -4,6 +4,8 @@
 
 const int BSDetector::RESULT_UNDETERMINED = -1;
 const int BSDetector::RESULT_OK = 0;
+const int BSDetector::RESULT_PRELIM_NO_DETECTION = 1;
+const int BSDetector::RESULT_PRELIM_TOO_FEW = 2;
 const int BSDetector::RESULT_INITIAL_NO_DETECTION = 11;
 const int BSDetector::RESULT_INITIAL_TOO_FEW = 12;
 const int BSDetector::RESULT_INITIAL_TOO_SPARSE = 13;
@@ -17,6 +19,7 @@ const int BSDetector::DEFAULT_BS_MIN_SIZE = 5;
 const int BSDetector::ABSOLUTE_BS_MIN_SIZE = 3;
 const int BSDetector::DEFAULT_CONNECT_MIN_SIZE = 5;
 const int BSDetector::DEFAULT_AUTO_RESOLUTION = 10;
+const int BSDetector::PRELIM_SIZE = 10;
 
 
 BSDetector::BSDetector ()
@@ -41,6 +44,7 @@ BSDetector::BSDetector ()
   densityTestOn = true;
   multiSelection = false;
   autoResol = DEFAULT_AUTO_RESOLUTION;
+  prelim = false;
 
   bsini = NULL;
   bsf = NULL;
@@ -161,13 +165,45 @@ void BSDetector::detect (const Pt2i &p1, const Pt2i &p2, Pt2i *p0)
   if (prefilteringOn) lsf1->clear ();
   if (filteringOn) lsf2->clear ();
   if (bsini != NULL) delete bsini;
+  bsini = NULL;
   if (bsf != NULL) delete bsf;
   bsf = NULL;
   if (p1.equals (p2)) return;
+  Pt2i pt1 (p1);
+  Pt2i pt2 (p2);
+
+  // Preliminary based on highest gradient without orientation constraint
+  //---------------------------------------------------------------------
+  if (prelim)
+  {
+    BlurredSegment *bs0 = bst1->fastTrack (p1, p2, p0);
+    if (bs0 == NULL || bs0->size () < bsMinSize)
+    {
+      resultValue = (bs0 == NULL ? RESULT_PRELIM_NO_DETECTION
+                                 : RESULT_PRELIM_TOO_FEW);
+      if (bs0 != NULL) delete bs0;
+      return;
+    }
+
+    Vr2i v0 = bs0->getSupportVector ();
+    Pt2i pc = bs0->getSegment()->centerOfIntersection (p1, p2);
+    v0.setOrthog ();
+    int l = v0.chessboard ();
+    int dx = (int) ((v0.x () * PRELIM_SIZE) / l);
+    int dy = (int) ((v0.y () * PRELIM_SIZE) / l);
+    if (dx * dy != 0)
+    {
+      pt1 = Pt2i (pc.x () + dx, pc.y () + dy);
+      pt2 = Pt2i (pc.x () - dx, pc.y () - dy);
+      p0 = NULL;
+    }
+    bst1->clear ();
+    delete bs0;
+  }
 
   // Initial detection based on highest gradient without orientation constraint
   //---------------------------------------------------------------------------
-  bsini = bst1->fastTrack (p1, p2, p0);
+  bsini = bst1->fastTrack (pt1, pt2, p0);
   if (bsini == NULL || bsini->size () < bsMinSize)
   {
     resultValue = (bsini == NULL ? RESULT_INITIAL_NO_DETECTION
@@ -179,7 +215,7 @@ void BSDetector::detect (const Pt2i &p1, const Pt2i &p2, Pt2i *p0)
   //-------------
   if (densityTestOn)
   {
-    DigitalStraightLine mydsl (p1, p2, DigitalStraightLine::DSL_NAIVE);
+    DigitalStraightLine mydsl (pt1, pt2, DigitalStraightLine::DSL_NAIVE);
     int mydrlf = mydsl.manhattan (bsini->getLastRight ())
                  - mydsl.manhattan (bsini->getLastLeft ());
     if (mydrlf < 0) mydrlf = -mydrlf; // Case of horizontal P1P2
@@ -225,7 +261,7 @@ void BSDetector::detect (const Pt2i &p1, const Pt2i &p2, Pt2i *p0)
       if (bswidth < minScanLength) bswidth = minScanLength;
     }
     scanwidth2 = bswidth;
-    pCenter = bsini->getSegment()->centerOfIntersection (p1, p2);
+    pCenter = bsini->getSegment()->centerOfIntersection (pt1, pt2);
   }
   bsf = bst2->fineTrack (pCenter, bsini->getSupportVector(),
                          scanwidth2, bswidth, gRef);
diff --git a/Code/Seg/BlurredSegment/bsdetector.h b/Code/Seg/BlurredSegment/bsdetector.h
index 468d5d17aca80228e7676c067d33846f09eec8d2..38a6569f34570c6d77a41cd87306620985755053 100755
--- a/Code/Seg/BlurredSegment/bsdetector.h
+++ b/Code/Seg/BlurredSegment/bsdetector.h
@@ -22,6 +22,10 @@ public:
   /** Extraction result : successful extraction. */
   static const int RESULT_OK;
   /** Extraction result : no initial detection (bsini == NULL). */
+  static const int RESULT_PRELIM_NO_DETECTION;
+  /** Extraction result : too few points at initial detection. */
+  static const int RESULT_PRELIM_TOO_FEW;
+  /** Extraction result : no initial detection (bsini == NULL). */
   static const int RESULT_INITIAL_NO_DETECTION;
   /** Extraction result : too few points at initial detection. */
   static const int RESULT_INITIAL_TOO_FEW;
@@ -166,6 +170,16 @@ public:
    */
   inline void switchAutoRestart () { bst2->switchAutoRestart (); }
 
+  /**
+   * \brief Returns the preliminary detection modality status.
+   */
+  inline bool isPreliminary () { return (prelim); }
+
+  /**
+   * \brief Switches preliminary detection modality.
+   */
+  inline void switchPreliminary () { prelim = ! prelim; }
+
   /**
    * \brief Returns the edge direction constraint status.
    *    +1 : Edge direction constrained to the initial direction.
@@ -390,6 +404,8 @@ private :
   static const int DEFAULT_CONNECT_MIN_SIZE;
   /** Default value for the automatic detection grid resolution. */
   static const int DEFAULT_AUTO_RESOLUTION;
+  /** Default value for half the preliminary stroke size. */
+  static const int PRELIM_SIZE;
 
 
   /** Direction constraint of the detected edge :
@@ -414,6 +430,8 @@ private :
   bool multiSelection;
   /** Grid resolution for the automatic extraction. */
   int autoResol;
+  /** Preliminary stage modality. */
+  bool prelim;
 
 
   /** Gradient map. */
diff --git a/Code/Seg/ImageTools/vr2i.h b/Code/Seg/ImageTools/vr2i.h
index 95b34edbd00c6ff18b439c758f5ec32341e96bf6..35cca7ab2135e9cf3a3b6c4df8c9febb380c2d83 100755
--- a/Code/Seg/ImageTools/vr2i.h
+++ b/Code/Seg/ImageTools/vr2i.h
@@ -39,19 +39,15 @@ public:
    * @fn int x ()
    * \brief Returns the vector abscissae.
    */
-  inline int x () const
-  {
-    return xv;
-  }
+  inline int x () const {
+    return xv; }
 
   /**
    * @fn int y ()
    * \brief Returns the vector ordinate.
    */
-  inline int y () const
-  {
-    return yv;
-  }
+  inline int y () const {
+    return yv; }
 
   /**
    * @fn void set (int x, int y)
@@ -59,11 +55,9 @@ public:
    * @param x new abscissae.
    * @param y new ordinate.
    */
-  inline void set (int x, int y)
-  {
+  inline void set (int x, int y) {
     xv = x;
-    yv = y;
-  }
+    yv = y; }
 
   /**
    * @fn int norm2 ()
@@ -71,10 +65,8 @@ public:
    * If intensity value holds on a byte, gradient holds on a short
    *   and gradient squared norm holds on a int.
    */
-  inline int norm2 () const
-  {
-    return (xv * xv + yv * yv);
-  }
+  inline int norm2 () const {
+    return (xv * xv + yv * yv); }
 
   /**
    * @fn int scalarProduct (Vr2i vec)
@@ -82,39 +74,39 @@ public:
    * If intensity value holds on a byte, scalar product (SP) holds on a short
    *   and squared SP holds on a int.
    */
-  inline int scalarProduct (Vr2i vec) const
-  {
-    return (xv * vec.xv + yv * vec.yv);
-  }
+  inline int scalarProduct (Vr2i vec) const {
+    return (xv * vec.xv + yv * vec.yv); }
 
   /**
    * @fn int squaredScalarProduct (Vr2i vec)
    * \brief Returns the sqaured scalar product with the given vector.
    */
-  inline int squaredScalarProduct (Vr2i vec) const
-  {
+  inline int squaredScalarProduct (Vr2i vec) const {
     return ((xv * vec.xv + yv * vec.yv)
-            * (xv * vec.xv + yv * vec.yv));
-  }
+            * (xv * vec.xv + yv * vec.yv)); }
 
   /**
    * @fn bool equals (Vr2i p)
    * \brief Checks equivalence to the given vector.
    * @param v the given vector.
    */
-  inline bool equals (Vr2i v) const
-  {
-    return (v.xv == xv && v.yv == yv);
-  }
+  inline bool equals (Vr2i v) const {
+    return (v.xv == xv && v.yv == yv); }
 
   /**
    * @fn int manhattan ()
    * \brief Returns the manhattan length of the vector.
    */
-  inline int manhattan () const
-  {
-    return ((xv > 0 ? xv : - xv) + (yv > 0 ? yv : - yv));
-  }
+  inline int manhattan () const {
+    return ((xv > 0 ? xv : - xv) + (yv > 0 ? yv : - yv)); }
+
+  /**
+   * @fn int chessboard ()
+   * \brief Returns the chessboard length of the vector.
+   */
+  inline int chessboard () const {
+    int x = (xv < 0 ? -xv : xv), y = (yv < 0 ? -yv : yv);
+    return (x > y ? x : y); }
 
   /**
    * @fn Vr2i orthog ()
@@ -122,24 +114,26 @@ public:
    */
   Vr2i orthog () const;
 
+  /**
+   * @fn void setOrthog ()
+   * \brief Returns a CCW orthogonal vector.
+   */
+  inline void setOrthog () { int tmp = xv; xv = - yv; yv = tmp; }
+
   /**
    * @fn bool orientedAs (Vr2i v)
    * \brief Returns true if v has the same orientation as the vector.
    */
-  inline bool orientedAs (Vr2i v) const
-  {
-    return (xv * v.xv + yv * v.yv >= 0);
-  }
+  inline bool orientedAs (Vr2i v) const {
+    return (xv * v.xv + yv * v.yv >= 0); }
 
   /**
    * @fn void invert ()
    * \brief Inverts the vector.
    */
-  inline void invert ()
-  {
+  inline void invert () {
     xv = -xv;
-    yv = -yv;
-  }
+    yv = -yv; }
 
   /**
    * @fn bool *steps (int *n)