refactored approxpoly
authorVadim Pisarevsky <vadim.pisarevsky@gmail.com>
Wed, 16 Jan 2013 20:11:08 +0000 (00:11 +0400)
committerVadim Pisarevsky <vadim.pisarevsky@gmail.com>
Wed, 16 Jan 2013 20:11:08 +0000 (00:11 +0400)
modules/core/include/opencv2/core/core.hpp
modules/core/include/opencv2/core/operations.hpp
modules/imgproc/src/approx.cpp
modules/imgproc/src/contours.cpp

index 94d7526..7afe332 100644 (file)
@@ -3107,6 +3107,10 @@ public:
     void allocate(size_t _size);
     //! deallocates the buffer if it was dynamically allocated
     void deallocate();
+    //! resizes the buffer and preserves the content
+       void resize(size_t _size);
+       //! returns the current buffer size
+       size_t size() const;
     //! returns pointer to the real buffer, stack-allocated or head-allocated
     operator _Tp* ();
     //! returns read-only pointer to the real buffer, stack-allocated or head-allocated
@@ -3116,7 +3120,7 @@ protected:
     //! pointer to the real buffer, can point to buf if the buffer is small enough
     _Tp* ptr;
     //! size of the real buffer
-    size_t size;
+    size_t sz;
     //! pre-allocated buffer
     _Tp buf[fixed_size+buffer_padding];
 };
index eaae0c0..c1928b7 100644 (file)
@@ -2537,13 +2537,13 @@ inline Point LineIterator::pos() const
 template<typename _Tp, size_t fixed_size> inline AutoBuffer<_Tp, fixed_size>::AutoBuffer()
 {
     ptr = buf;
-    size = fixed_size;
+    sz = fixed_size;
 }
 
 template<typename _Tp, size_t fixed_size> inline AutoBuffer<_Tp, fixed_size>::AutoBuffer(size_t _size)
 {
     ptr = buf;
-    size = fixed_size;
+    sz = fixed_size;
     allocate(_size);
 }
 
@@ -2552,13 +2552,16 @@ template<typename _Tp, size_t fixed_size> inline AutoBuffer<_Tp, fixed_size>::~A
 
 template<typename _Tp, size_t fixed_size> inline void AutoBuffer<_Tp, fixed_size>::allocate(size_t _size)
 {
-    if(_size <= size)
+    if(_size <= sz)
+    {
+        sz = _size;
         return;
+    }
     deallocate();
     if(_size > fixed_size)
     {
-        ptr = cv::allocate<_Tp>(_size);
-        size = _size;
+        ptr = new _Tp[_size];
+        sz = _size;
     }
 }
 
@@ -2566,12 +2569,38 @@ template<typename _Tp, size_t fixed_size> inline void AutoBuffer<_Tp, fixed_size
 {
     if( ptr != buf )
     {
-        cv::deallocate<_Tp>(ptr, size);
+        delete[] ptr;
         ptr = buf;
-        size = fixed_size;
+        sz = 0;
     }
 }
 
+template<typename _Tp, size_t fixed_size> inline void AutoBuffer<_Tp, fixed_size>::resize(size_t _size)
+{
+    if(_size <= sz)
+    {
+        sz = _size;
+        return;
+    }
+    size_t i, prevsize = sz, minsize = MIN(prevsize, _size);
+    _Tp* prevptr = ptr;
+    
+    ptr = _size > fixed_size ? new _Tp[_size] : buf;
+    sz = _size;
+    
+    if( ptr != prevptr )
+        for( i = 0; i < minsize; i++ )
+            ptr[i] = prevptr[i];
+    for( i = prevsize; i < _size; i++ )
+        ptr[i] = _Tp();
+    
+    if( prevptr != buf )
+        delete[] prevptr;
+}
+
+template<typename _Tp, size_t fixed_size> inline size_t AutoBuffer<_Tp, fixed_size>::size() const
+{ return sz; }
+
 template<typename _Tp, size_t fixed_size> inline AutoBuffer<_Tp, fixed_size>::operator _Tp* ()
 { return ptr; }
 
index c2831ca..dc9bee2 100644 (file)
@@ -469,48 +469,59 @@ cvApproxChains( CvSeq*              src_seq,
 
 /* Ramer-Douglas-Peucker algorithm for polygon simplification */
 
-/* the version for integer point coordinates */
-template<typename T> static CvSeq*
-icvApproxPolyDP( CvSeq* src_contour, int header_size,
-                 CvMemStorage* storage, double eps )
+namespace cv
 {
+
+template<typename T> static int
+approxPolyDP_( const Point_<T>* src_contour, int count0, Point_<T>* dst_contour,
+              bool is_closed0, double eps, AutoBuffer<Range>* _stack )
+{
+    #define PUSH_SLICE(slice) \
+        if( top >= stacksz ) \
+        { \
+            _stack->resize(stacksz*3/2); \
+            stack = *_stack; \
+            stacksz = _stack->size(); \
+        } \
+        stack[top++] = slice
+
+    #define READ_PT(pt, pos) \
+        pt = src_contour[pos]; \
+        if( ++pos >= count ) pos = 0
+
+    #define READ_DST_PT(pt, pos) \
+        pt = dst_contour[pos]; \
+        if( ++pos >= count ) pos = 0
+
+    #define WRITE_PT(pt) \
+        dst_contour[new_count++] = pt
+
     typedef cv::Point_<T> PT;
     int             init_iters = 3;
-    CvSlice         slice = {0, 0}, right_slice = {0, 0};
-    CvSeqReader     reader, reader2;
-    CvSeqWriter     writer;
-    PT              start_pt(-1000000, -1000000), end_pt(0, 0), pt(0,0);
-    int             i = 0, j, count = src_contour->total, new_count;
-    int             is_closed = CV_IS_SEQ_CLOSED( src_contour );
+    Range           slice(0, 0), right_slice(0, 0);
+    PT              start_pt((T)-1000000, (T)-1000000), end_pt(0, 0), pt(0,0);
+    int             i = 0, j, pos = 0, wpos, count = count0, new_count=0;
+    int             is_closed = is_closed0;
     bool            le_eps = false;
-    CvMemStorage*   temp_storage = 0;
-    CvSeq*          stack = 0;
-    CvSeq*          dst_contour;
+    size_t top = 0, stacksz = _stack->size();
+    Range*          stack = *_stack;
 
-    assert( CV_SEQ_ELTYPE(src_contour) == cv::DataType<PT>::type );
-    cvStartWriteSeq( src_contour->flags, header_size, sizeof(pt), storage, &writer );
+    if( count == 0  )
+        return 0;
 
-    if( src_contour->total == 0  )
-        return cvEndWriteSeq( &writer );
-
-    temp_storage = cvCreateChildMemStorage( storage );
-
-    assert( src_contour->first != 0 );
-    stack = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSlice), temp_storage );
     eps *= eps;
-    cvStartReadSeq( src_contour, &reader, 0 );
 
     if( !is_closed )
     {
-        right_slice.start_index = count;
-        end_pt = *(PT*)(reader.ptr);
-        start_pt = *(PT*)cvGetSeqElem( src_contour, -1 );
+        right_slice.start = count;
+        end_pt = src_contour[0];
+        start_pt = src_contour[count-1];
 
         if( start_pt.x != end_pt.x || start_pt.y != end_pt.y )
         {
-            slice.start_index = 0;
-            slice.end_index = count - 1;
-            cvSeqPush( stack, &slice );
+            slice.start = 0;
+            slice.end = count - 1;
+            PUSH_SLICE(slice);
         }
         else
         {
@@ -521,20 +532,20 @@ icvApproxPolyDP( CvSeq* src_contour, int header_size,
 
     if( is_closed )
     {
-        /* 1. Find approximately two farthest points of the contour */
-        right_slice.start_index = 0;
+        // 1. Find approximately two farthest points of the contour
+        right_slice.start = 0;
 
         for( i = 0; i < init_iters; i++ )
         {
             double dist, max_dist = 0;
-            cvSetSeqReaderPos( &reader, right_slice.start_index, 1 );
-            CV_READ_SEQ_ELEM( start_pt, reader );   /* read the first point */
+            pos = (pos + right_slice.start) % count;
+            READ_PT(start_pt, pos);
 
             for( j = 1; j < count; j++ )
             {
                 double dx, dy;
 
-                CV_READ_SEQ_ELEM( pt, reader );
+                READ_PT(pt, pos);
                 dx = pt.x - start_pt.x;
                 dy = pt.y - start_pt.y;
 
@@ -543,43 +554,35 @@ icvApproxPolyDP( CvSeq* src_contour, int header_size,
                 if( dist > max_dist )
                 {
                     max_dist = dist;
-                    right_slice.start_index = j;
+                    right_slice.start = j;
                 }
             }
 
             le_eps = max_dist <= eps;
         }
 
-        /* 2. initialize the stack */
+        // 2. initialize the stack
         if( !le_eps )
         {
-            slice.start_index = cvGetSeqReaderPos( &reader );
-            slice.end_index = right_slice.start_index += slice.start_index;
-
-            right_slice.start_index -= right_slice.start_index >= count ? count : 0;
-            right_slice.end_index = slice.start_index;
-            if( right_slice.end_index < right_slice.start_index )
-                right_slice.end_index += count;
+            right_slice.end = slice.start = pos % count;
+            slice.end = right_slice.start = (right_slice.start + slice.start) % count;
 
-            cvSeqPush( stack, &right_slice );
-            cvSeqPush( stack, &slice );
+            PUSH_SLICE(right_slice);
+            PUSH_SLICE(slice);
         }
         else
-            CV_WRITE_SEQ_ELEM( start_pt, writer );
+            WRITE_PT(start_pt);
     }
 
-    /* 3. run recursive process */
-    while( stack->total != 0 )
+    // 3. run recursive process
+    while( top > 0 )
     {
-        cvSeqPop( stack, &slice );
+        slice = stack[--top];
+        end_pt = src_contour[slice.end];
+        pos = slice.start;
+        READ_PT(start_pt, pos);
 
-        cvSetSeqReaderPos( &reader, slice.end_index );
-        CV_READ_SEQ_ELEM( end_pt, reader );
-
-        cvSetSeqReaderPos( &reader, slice.start_index );
-        CV_READ_SEQ_ELEM( start_pt, reader );
-
-        if( slice.end_index > slice.start_index + 1 )
+        if( pos != slice.end )
         {
             double dx, dy, dist, max_dist = 0;
 
@@ -588,15 +591,15 @@ icvApproxPolyDP( CvSeq* src_contour, int header_size,
 
             assert( dx != 0 || dy != 0 );
 
-            for( i = slice.start_index + 1; i < slice.end_index; i++ )
+            while( pos != slice.end )
             {
-                CV_READ_SEQ_ELEM( pt, reader );
+                READ_PT(pt, pos);
                 dist = fabs((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy);
 
                 if( dist > max_dist )
                 {
                     max_dist = dist;
-                    right_slice.start_index = i;
+                    right_slice.start = (pos+count-1)%count;
                 }
             }
 
@@ -604,84 +607,106 @@ icvApproxPolyDP( CvSeq* src_contour, int header_size,
         }
         else
         {
-            assert( slice.end_index > slice.start_index );
             le_eps = true;
-            /* read starting point */
-            cvSetSeqReaderPos( &reader, slice.start_index );
-            CV_READ_SEQ_ELEM( start_pt, reader );
+            // read starting point
+            start_pt = src_contour[slice.start];
         }
 
         if( le_eps )
         {
-            CV_WRITE_SEQ_ELEM( start_pt, writer );
+            WRITE_PT(start_pt);
         }
         else
         {
-            right_slice.end_index = slice.end_index;
-            slice.end_index = right_slice.start_index;
-            cvSeqPush( stack, &right_slice );
-            cvSeqPush( stack, &slice );
+            right_slice.end = slice.end;
+            slice.end = right_slice.start;
+            PUSH_SLICE(right_slice);
+            PUSH_SLICE(slice);
         }
     }
 
-    is_closed = CV_IS_SEQ_CLOSED( src_contour );
     if( !is_closed )
-        CV_WRITE_SEQ_ELEM( end_pt, writer );
-
-    dst_contour = cvEndWriteSeq( &writer );
+        WRITE_PT( src_contour[count-1] );
 
     // last stage: do final clean-up of the approximated contour -
     // remove extra points on the [almost] stright lines.
+    is_closed = is_closed0;
+    count = new_count;
+    pos = is_closed ? count - 1 : 0;
+    READ_DST_PT(start_pt, pos);
+    wpos = pos;
+    READ_DST_PT(pt, pos);
 
-    cvStartReadSeq( dst_contour, &reader, is_closed );
-    CV_READ_SEQ_ELEM( start_pt, reader );
-
-    reader2 = reader;
-    CV_READ_SEQ_ELEM( pt, reader );
-
-    new_count = count = dst_contour->total;
     for( i = !is_closed; i < count - !is_closed && new_count > 2; i++ )
     {
         double dx, dy, dist, successive_inner_product;
-        CV_READ_SEQ_ELEM( end_pt, reader );
+        READ_DST_PT( end_pt, pos );
 
         dx = end_pt.x - start_pt.x;
         dy = end_pt.y - start_pt.y;
         dist = fabs((pt.x - start_pt.x)*dy - (pt.y - start_pt.y)*dx);
         successive_inner_product = (pt.x - start_pt.x) * (end_pt.x - pt.x) +
-            (pt.y - start_pt.y) * (end_pt.y - pt.y);
+        (pt.y - start_pt.y) * (end_pt.y - pt.y);
 
         if( dist * dist <= 0.5*eps*(dx*dx + dy*dy) && dx != 0 && dy != 0 &&
-            successive_inner_product >= 0 )
+           successive_inner_product >= 0 )
         {
             new_count--;
-            *((PT*)reader2.ptr) = start_pt = end_pt;
-            CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 );
-            CV_READ_SEQ_ELEM( pt, reader );
+            dst_contour[wpos] = start_pt = end_pt;
+            if(++wpos >= count) wpos = 0;
+            READ_DST_PT(pt, pos);
             i++;
             continue;
         }
-        *((PT*)reader2.ptr) = start_pt = pt;
-        CV_NEXT_SEQ_ELEM( sizeof(pt), reader2 );
+        dst_contour[wpos] = start_pt = pt;
+        if(++wpos >= count) wpos = 0;
         pt = end_pt;
     }
 
     if( !is_closed )
-        *((PT*)reader2.ptr) = pt;
+        dst_contour[wpos] = pt;
 
-    if( new_count < count )
-        cvSeqPopMulti( dst_contour, 0, count - new_count );
+    return new_count;
+}
 
-    cvReleaseMemStorage( &temp_storage );
-    return dst_contour;
+}
+
+void cv::approxPolyDP( InputArray _curve, OutputArray _approxCurve,
+                      double epsilon, bool closed )
+{
+    Mat curve = _curve.getMat();
+    int npoints = curve.checkVector(2), depth = curve.depth();
+    CV_Assert( npoints >= 0 && (depth == CV_32S || depth == CV_32F));
+
+    if( npoints == 0 )
+    {
+        _approxCurve.release();
+        return;
+    }
+
+    AutoBuffer<Point> _buf(npoints);
+    AutoBuffer<Range> _stack(npoints);
+    Point* buf = _buf;
+    int nout = 0;
+
+    if( depth == CV_32S )
+        nout = approxPolyDP_(curve.ptr<Point>(), npoints, buf, closed, epsilon, &_stack);
+    else if( depth == CV_32F )
+        nout = approxPolyDP_(curve.ptr<Point2f>(), npoints, (Point2f*)buf, closed, epsilon, &_stack);
+    else
+        CV_Error( CV_StsUnsupportedFormat, "" );
+
+    Mat(nout, 1, CV_MAKETYPE(depth, 2), buf).copyTo(_approxCurve);
 }
 
 
 CV_IMPL CvSeq*
-cvApproxPoly( const void*  array, int  header_size,
-              CvMemStorage*  storage, int  method,
-              double  parameter, int parameter2 )
+cvApproxPoly( const void* array, int header_size,
+             CvMemStorage* storage, int method,
+             double parameter, int parameter2 )
 {
+    cv::AutoBuffer<cv::Point> _buf;
+    cv::AutoBuffer<cv::Range> stack(100);
     CvSeq* dst_seq = 0;
     CvSeq *prev_contour = 0, *parent = 0;
     CvContour contour_header;
@@ -703,8 +728,8 @@ cvApproxPoly( const void*  array, int  header_size,
     else
     {
         src_seq = cvPointSeqFromMat(
-            CV_SEQ_KIND_CURVE | (parameter2 ? CV_SEQ_FLAG_CLOSED : 0),
-            array, &contour_header, &block );
+                                    CV_SEQ_KIND_CURVE | (parameter2 ? CV_SEQ_FLAG_CLOSED : 0),
+                                    array, &contour_header, &block );
     }
 
     if( !storage )
@@ -712,7 +737,7 @@ cvApproxPoly( const void*  array, int  header_size,
 
     if( header_size < 0 )
         CV_Error( CV_StsOutOfRange, "header_size is negative. "
-                  "Pass 0 to make the destination header_size == input header_size" );
+                 "Pass 0 to make the destination header_size == input header_size" );
 
     if( header_size == 0 )
         header_size = src_seq->header_size;
@@ -722,7 +747,7 @@ cvApproxPoly( const void*  array, int  header_size,
         if( CV_IS_SEQ_CHAIN( src_seq ))
         {
             CV_Error( CV_StsBadArg, "Input curves are not polygonal. "
-                                    "Use cvApproxChains first" );
+                     "Use cvApproxChains first" );
         }
         else
         {
@@ -749,13 +774,34 @@ cvApproxPoly( const void*  array, int  header_size,
             if( parameter < 0 )
                 CV_Error( CV_StsOutOfRange, "Accuracy must be non-negative" );
 
+            CV_Assert( CV_SEQ_ELTYPE(src_seq) == CV_32SC2 ||
+                      CV_SEQ_ELTYPE(src_seq) == CV_32FC2 );
+
+            {
+            int npoints = src_seq->total, nout = 0;
+            _buf.allocate(npoints*2);
+            cv::Point *src = _buf, *dst = src + npoints;
+            bool closed = CV_IS_SEQ_CLOSED(src_seq);
+
+            if( src_seq->first->next == src_seq->first )
+                src = (cv::Point*)src_seq->first->data;
+            else
+                cvCvtSeqToArray(src_seq, src);
+
             if( CV_SEQ_ELTYPE(src_seq) == CV_32SC2 )
-                contour = icvApproxPolyDP<int>( src_seq, header_size, storage, parameter );
+                nout = cv::approxPolyDP_(src, npoints, dst, closed, parameter, &stack);
+            else if( CV_SEQ_ELTYPE(src_seq) == CV_32FC2 )
+                nout = cv::approxPolyDP_((cv::Point2f*)src, npoints,
+                                         (cv::Point2f*)dst, closed, parameter, &stack);
             else
-                contour = icvApproxPolyDP<float>( src_seq, header_size, storage, parameter );
+                CV_Error( CV_StsUnsupportedFormat, "" );
+
+            contour = cvCreateSeq( src_seq->flags, header_size,
+                                  src_seq->elem_size, storage );
+            cvSeqPushMulti(contour, dst, nout);
+            }
             break;
         default:
-            assert(0);
             CV_Error( CV_StsBadArg, "Invalid approximation method" );
         }
 
index c290385..01f0393 100644 (file)
@@ -1753,23 +1753,6 @@ void cv::findContours( InputOutputArray _image, OutputArrayOfArrays _contours,
     findContours(_image, _contours, noArray(), mode, method, offset);
 }
 
-void cv::approxPolyDP( InputArray _curve, OutputArray _approxCurve,
-                       double epsilon, bool closed )
-{
-    Mat curve = _curve.getMat();
-    int npoints = curve.checkVector(2), depth = curve.depth();
-    CV_Assert( npoints >= 0 && (depth == CV_32S || depth == CV_32F));
-    CvMat _ccurve = curve;
-    MemStorage storage(cvCreateMemStorage());
-    CvSeq* result = cvApproxPoly(&_ccurve, sizeof(CvContour), storage, CV_POLY_APPROX_DP, epsilon, closed);
-    if( result->total > 0 )
-    {
-        _approxCurve.create(result->total, 1, CV_MAKETYPE(curve.depth(), 2), -1, true);
-        cvCvtSeqToArray(result, _approxCurve.getMat().data );
-    }
-}
-
-
 double cv::arcLength( InputArray _curve, bool closed )
 {
     Mat curve = _curve.getMat();