opencv/modules/imgproc/src/approx.cpp
Maksim Shabunin 8cbdd0c833
Merge pull request #25075 from mshabunin:cleanup-imgproc-1
C-API cleanup: apps, imgproc_c and some constants #25075

Merge with https://github.com/opencv/opencv_contrib/pull/3642

* Removed obsolete apps - traincascade and createsamples (please use older OpenCV versions if you need them). These apps relied heavily on C-API
* removed all mentions of imgproc C-API headers (imgproc_c.h, types_c.h) - they were empty, included core C-API headers
* replaced usage of several C constants with C++ ones (error codes, norm modes, RNG modes, PCA modes, ...) - most part of this PR (split into two parts - all modules and calib+3d - for easier backporting)
* removed imgproc C-API headers (as separate commit, so that other changes could be backported to 4.x)

Most of these changes can be backported to 4.x.
2024-03-05 12:18:31 +03:00

653 lines
18 KiB
C++

/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
/****************************************************************************************\
* Chain Approximation *
\****************************************************************************************/
typedef struct _CvPtInfo
{
CvPoint pt;
int k; /* support region */
int s; /* curvature value */
struct _CvPtInfo *next;
}
_CvPtInfo;
static const CvPoint icvCodeDeltas[8] =
{ {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1} };
/** Freeman chain reader state */
typedef struct CvChainPtReader
{
CV_SEQ_READER_FIELDS()
char code;
CvPoint pt;
schar deltas[8][2];
}
CvChainPtReader;
static void cvStartReadChainPoints( CvChain * chain, CvChainPtReader * reader )
{
int i;
if( !chain || !reader )
CV_Error( cv::Error::StsNullPtr, "" );
if( chain->elem_size != 1 || chain->header_size < (int)sizeof(CvChain))
CV_Error( cv::Error::StsBadSize, "" );
cvStartReadSeq( (CvSeq *) chain, (CvSeqReader *) reader, 0 );
reader->pt = chain->origin;
for( i = 0; i < 8; i++ )
{
reader->deltas[i][0] = (schar) icvCodeDeltas[i].x;
reader->deltas[i][1] = (schar) icvCodeDeltas[i].y;
}
}
/* curvature: 0 - 1-curvature, 1 - k-cosine curvature. */
CvSeq* icvApproximateChainTC89( CvChain* chain, int header_size,
CvMemStorage* storage, int method )
{
static const int abs_diff[] = { 1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1 };
cv::AutoBuffer<_CvPtInfo> buf(chain->total + 8);
_CvPtInfo temp;
_CvPtInfo *array = buf.data(), *first = 0, *current = 0, *prev_current = 0;
int i, j, i1, i2, s, len;
int count = chain->total;
CvChainPtReader reader;
CvSeqWriter writer;
CvPoint pt = chain->origin;
CV_Assert( CV_IS_SEQ_CHAIN_CONTOUR( chain ));
CV_Assert( header_size >= (int)sizeof(CvContour) );
cvStartWriteSeq( (chain->flags & ~CV_SEQ_ELTYPE_MASK) | CV_SEQ_ELTYPE_POINT,
header_size, sizeof( CvPoint ), storage, &writer );
if( chain->total == 0 )
{
CV_WRITE_SEQ_ELEM( pt, writer );
return cvEndWriteSeq( &writer );
}
reader.code = 0;
cvStartReadChainPoints( chain, &reader );
temp.next = 0;
current = &temp;
/* Pass 0.
Restores all the digital curve points from the chain code.
Removes the points (from the resultant polygon)
that have zero 1-curvature */
for( i = 0; i < count; i++ )
{
int prev_code = *reader.prev_elem;
reader.prev_elem = reader.ptr;
CV_READ_CHAIN_POINT( pt, reader );
/* calc 1-curvature */
s = abs_diff[reader.code - prev_code + 7];
if( method <= cv::CHAIN_APPROX_SIMPLE )
{
if( method == cv::CHAIN_APPROX_NONE || s != 0 )
{
CV_WRITE_SEQ_ELEM( pt, writer );
}
}
else
{
if( s != 0 )
current = current->next = array + i;
array[i].s = s;
array[i].pt = pt;
}
}
//CV_Assert( pt.x == chain->origin.x && pt.y == chain->origin.y );
if( method <= cv::CHAIN_APPROX_SIMPLE )
return cvEndWriteSeq( &writer );
current->next = 0;
len = i;
current = temp.next;
CV_Assert( current );
/* Pass 1.
Determines support region for all the remained points */
do
{
cv::Point2i pt0;
int k, l = 0, d_num = 0;
i = (int)(current - array);
pt0 = array[i].pt;
/* determine support region */
for( k = 1;; k++ )
{
int lk, dk_num;
int dx, dy;
Cv32suf d;
CV_Assert( k <= len );
/* calc indices */
i1 = i - k;
i1 += i1 < 0 ? len : 0;
i2 = i + k;
i2 -= i2 >= len ? len : 0;
dx = array[i2].pt.x - array[i1].pt.x;
dy = array[i2].pt.y - array[i1].pt.y;
/* distance between p_(i - k) and p_(i + k) */
lk = dx * dx + dy * dy;
/* distance between p_i and the line (p_(i-k), p_(i+k)) */
dk_num = (pt0.x - array[i1].pt.x) * dy - (pt0.y - array[i1].pt.y) * dx;
d.f = (float) (((double) d_num) * lk - ((double) dk_num) * l);
if( k > 1 && (l >= lk || ((d_num > 0 && d.i <= 0) || (d_num < 0 && d.i >= 0))))
break;
d_num = dk_num;
l = lk;
}
current->k = --k;
/* determine cosine curvature if it should be used */
if( method == cv::CHAIN_APPROX_TC89_KCOS )
{
/* calc k-cosine curvature */
for( j = k, s = 0; j > 0; j-- )
{
double temp_num;
int dx1, dy1, dx2, dy2;
Cv32suf sk;
i1 = i - j;
i1 += i1 < 0 ? len : 0;
i2 = i + j;
i2 -= i2 >= len ? len : 0;
dx1 = array[i1].pt.x - pt0.x;
dy1 = array[i1].pt.y - pt0.y;
dx2 = array[i2].pt.x - pt0.x;
dy2 = array[i2].pt.y - pt0.y;
if( (dx1 | dy1) == 0 || (dx2 | dy2) == 0 )
break;
temp_num = dx1 * dx2 + dy1 * dy2;
temp_num =
(float) (temp_num /
sqrt( ((double)dx1 * dx1 + (double)dy1 * dy1) *
((double)dx2 * dx2 + (double)dy2 * dy2) ));
sk.f = (float) (temp_num + 1.1);
CV_Assert( 0 <= sk.f && sk.f <= 2.2 );
if( j < k && sk.i <= s )
break;
s = sk.i;
}
current->s = s;
}
current = current->next;
}
while( current != 0 );
prev_current = &temp;
current = temp.next;
/* Pass 2.
Performs non-maxima suppression */
do
{
int k2 = current->k >> 1;
s = current->s;
i = (int)(current - array);
for( j = 1; j <= k2; j++ )
{
i2 = i - j;
i2 += i2 < 0 ? len : 0;
if( array[i2].s > s )
break;
i2 = i + j;
i2 -= i2 >= len ? len : 0;
if( array[i2].s > s )
break;
}
if( j <= k2 ) /* exclude point */
{
prev_current->next = current->next;
current->s = 0; /* "clear" point */
}
else
prev_current = current;
current = current->next;
}
while( current != 0 );
/* Pass 3.
Removes non-dominant points with 1-length support region */
current = temp.next;
CV_Assert( current );
prev_current = &temp;
do
{
if( current->k == 1 )
{
s = current->s;
i = (int)(current - array);
i1 = i - 1;
i1 += i1 < 0 ? len : 0;
i2 = i + 1;
i2 -= i2 >= len ? len : 0;
if( s <= array[i1].s || s <= array[i2].s )
{
prev_current->next = current->next;
current->s = 0;
}
else
prev_current = current;
}
else
prev_current = current;
current = current->next;
}
while( current != 0 );
if( method == cv::CHAIN_APPROX_TC89_KCOS )
goto copy_vect;
/* Pass 4.
Cleans remained couples of points */
CV_Assert( temp.next );
if( array[0].s != 0 && array[len - 1].s != 0 ) /* specific case */
{
for( i1 = 1; i1 < len && array[i1].s != 0; i1++ )
{
array[i1 - 1].s = 0;
}
if( i1 == len )
goto copy_vect; /* all points survived */
i1--;
for( i2 = len - 2; i2 > 0 && array[i2].s != 0; i2-- )
{
array[i2].next = 0;
array[i2 + 1].s = 0;
}
i2++;
if( i1 == 0 && i2 == len - 1 ) /* only two points */
{
i1 = (int)(array[0].next - array);
array[len] = array[0]; /* move to the end */
array[len].next = 0;
array[len - 1].next = array + len;
}
temp.next = array + i1;
}
current = temp.next;
first = prev_current = &temp;
count = 1;
/* do last pass */
do
{
if( current->next == 0 || current->next - current != 1 )
{
if( count >= 2 )
{
if( count == 2 )
{
int s1 = prev_current->s;
int s2 = current->s;
if( s1 > s2 || (s1 == s2 && prev_current->k <= current->k) )
/* remove second */
prev_current->next = current->next;
else
/* remove first */
first->next = current;
}
else
first->next->next = current;
}
first = current;
count = 1;
}
else
count++;
prev_current = current;
current = current->next;
}
while( current != 0 );
copy_vect:
// gather points
current = temp.next;
CV_Assert( current );
do
{
CV_WRITE_SEQ_ELEM( current->pt, writer );
current = current->next;
}
while( current != 0 );
return cvEndWriteSeq( &writer );
}
/****************************************************************************************\
* Polygonal Approximation *
\****************************************************************************************/
/* Ramer-Douglas-Peucker algorithm for polygon simplification */
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.data(); \
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;
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;
size_t top = 0, stacksz = _stack.size();
Range* stack = _stack.data();
if( count == 0 )
return 0;
eps *= eps;
if( !is_closed )
{
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 = 0;
slice.end = count - 1;
PUSH_SLICE(slice);
}
else
{
is_closed = 1;
init_iters = 1;
}
}
if( is_closed )
{
// 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;
pos = (pos + right_slice.start) % count;
READ_PT(start_pt, pos);
for( j = 1; j < count; j++ )
{
double dx, dy;
READ_PT(pt, pos);
dx = pt.x - start_pt.x;
dy = pt.y - start_pt.y;
dist = dx * dx + dy * dy;
if( dist > max_dist )
{
max_dist = dist;
right_slice.start = j;
}
}
le_eps = max_dist <= eps;
}
// 2. initialize the stack
if( !le_eps )
{
right_slice.end = slice.start = pos % count;
slice.end = right_slice.start = (right_slice.start + slice.start) % count;
PUSH_SLICE(right_slice);
PUSH_SLICE(slice);
}
else
WRITE_PT(start_pt);
}
// 3. run recursive process
while( top > 0 )
{
slice = stack[--top];
end_pt = src_contour[slice.end];
pos = slice.start;
READ_PT(start_pt, pos);
if( pos != slice.end )
{
double dx, dy, dist, max_dist = 0;
dx = end_pt.x - start_pt.x;
dy = end_pt.y - start_pt.y;
CV_Assert( dx != 0 || dy != 0 );
while( pos != slice.end )
{
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 = (pos+count-1)%count;
}
}
le_eps = max_dist * max_dist <= eps * (dx * dx + dy * dy);
}
else
{
le_eps = true;
// read starting point
start_pt = src_contour[slice.start];
}
if( le_eps )
{
WRITE_PT(start_pt);
}
else
{
right_slice.end = slice.end;
slice.end = right_slice.start;
PUSH_SLICE(right_slice);
PUSH_SLICE(slice);
}
}
if( !is_closed )
WRITE_PT( src_contour[count-1] );
// last stage: do final clean-up of the approximated contour -
// remove extra points on the [almost] straight 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);
for( i = !is_closed; i < count - !is_closed && new_count > 2; i++ )
{
double dx, dy, dist, successive_inner_product;
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);
if( dist * dist <= 0.5*eps*(dx*dx + dy*dy) && dx != 0 && dy != 0 &&
successive_inner_product >= 0 )
{
new_count--;
dst_contour[wpos] = start_pt = end_pt;
if(++wpos >= count) wpos = 0;
READ_DST_PT(pt, pos);
i++;
continue;
}
dst_contour[wpos] = start_pt = pt;
if(++wpos >= count) wpos = 0;
pt = end_pt;
}
if( !is_closed )
dst_contour[wpos] = pt;
return new_count;
}
}
void cv::approxPolyDP( InputArray _curve, OutputArray _approxCurve,
double epsilon, bool closed )
{
CV_INSTRUMENT_REGION();
//Prevent unreasonable error values (Douglas-Peucker algorithm)
//from being used.
if (epsilon < 0.0 || !(epsilon < 1e30))
{
CV_Error(cv::Error::StsOutOfRange, "Epsilon not valid.");
}
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.data();
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::Error::StsUnsupportedFormat, "" );
Mat(nout, 1, CV_MAKETYPE(depth, 2), buf).copyTo(_approxCurve);
}