From e3941d0965172a4587801e84088e1b3ffde8211b Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Thu, 17 Jan 2013 00:11:08 +0400 Subject: [PATCH] refactored approxpoly --- modules/core/include/opencv2/core/core.hpp | 6 +- .../core/include/opencv2/core/operations.hpp | 43 ++- modules/imgproc/src/approx.cpp | 252 +++++++++++------- modules/imgproc/src/contours.cpp | 17 -- 4 files changed, 190 insertions(+), 128 deletions(-) diff --git a/modules/core/include/opencv2/core/core.hpp b/modules/core/include/opencv2/core/core.hpp index 94d7526aaf..7afe3320a3 100644 --- a/modules/core/include/opencv2/core/core.hpp +++ b/modules/core/include/opencv2/core/core.hpp @@ -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]; }; diff --git a/modules/core/include/opencv2/core/operations.hpp b/modules/core/include/opencv2/core/operations.hpp index eaae0c0b18..c1928b787e 100644 --- a/modules/core/include/opencv2/core/operations.hpp +++ b/modules/core/include/opencv2/core/operations.hpp @@ -2537,13 +2537,13 @@ inline Point LineIterator::pos() const template inline AutoBuffer<_Tp, fixed_size>::AutoBuffer() { ptr = buf; - size = fixed_size; + sz = fixed_size; } template inline AutoBuffer<_Tp, fixed_size>::AutoBuffer(size_t _size) { ptr = buf; - size = fixed_size; + sz = fixed_size; allocate(_size); } @@ -2552,13 +2552,16 @@ template inline AutoBuffer<_Tp, fixed_size>::~A template 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 inline void AutoBuffer<_Tp, fixed_size { if( ptr != buf ) { - cv::deallocate<_Tp>(ptr, size); + delete[] ptr; ptr = buf; - size = fixed_size; + sz = 0; } } +template 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 inline size_t AutoBuffer<_Tp, fixed_size>::size() const +{ return sz; } + template inline AutoBuffer<_Tp, fixed_size>::operator _Tp* () { return ptr; } diff --git a/modules/imgproc/src/approx.cpp b/modules/imgproc/src/approx.cpp index c2831ca70d..dc9bee2f47 100644 --- a/modules/imgproc/src/approx.cpp +++ b/modules/imgproc/src/approx.cpp @@ -469,48 +469,59 @@ cvApproxChains( CvSeq* src_seq, /* Ramer-Douglas-Peucker algorithm for polygon simplification */ -/* the version for integer point coordinates */ -template static CvSeq* -icvApproxPolyDP( CvSeq* src_contour, int header_size, - CvMemStorage* storage, double eps ) +namespace cv { + +template static int +approxPolyDP_( const Point_* src_contour, int count0, Point_* dst_contour, + bool is_closed0, double eps, AutoBuffer* _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_ 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::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.end = slice.start = pos % count; + slice.end = right_slice.start = (right_slice.start + slice.start) % count; - 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; - - 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 _buf(npoints); + AutoBuffer _stack(npoints); + Point* buf = _buf; + int nout = 0; + + if( depth == CV_32S ) + nout = approxPolyDP_(curve.ptr(), npoints, buf, closed, epsilon, &_stack); + else if( depth == CV_32F ) + nout = approxPolyDP_(curve.ptr(), 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 _buf; + cv::AutoBuffer 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" ); - if( CV_SEQ_ELTYPE(src_seq) == CV_32SC2 ) - contour = icvApproxPolyDP( src_seq, header_size, storage, parameter ); + 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 - contour = icvApproxPolyDP( src_seq, header_size, storage, parameter ); + cvCvtSeqToArray(src_seq, src); + + if( CV_SEQ_ELTYPE(src_seq) == CV_32SC2 ) + 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 + 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" ); } diff --git a/modules/imgproc/src/contours.cpp b/modules/imgproc/src/contours.cpp index c290385cc7..01f0393779 100644 --- a/modules/imgproc/src/contours.cpp +++ b/modules/imgproc/src/contours.cpp @@ -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();