opencv中的approxPolyDP可以用来找轮廓的角点,但是eps参数设置不是很灵活。通过返回距离信息,可灵活提取指定数量角点
typedef struct _SliceRange{
int start;
int end;
}SliceRange;
double opencv_approxPolyDP( const std::vector<cv::Point>& src_contour, std::vector<cv::Point>& dst_contour, double eps, bool is_closed0){
#define PUSH_SLICE(slice) \
if( top >= stacksz ) \
{ \
stack.resize(stacksz*3/2); \
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\
int count0 = (int)src_contour.size();
std::vector<SliceRange> stack(count0);
dst_contour.resize(count0);
double slice_max_dist = 0; //slice失败的最大距离
typedef cv::Point PT;
int init_iters = 3;
SliceRange slice, right_slice;
PT start_pt(-1000000, -1000000), end_pt(0, 0), pt(0,0);
int i = 0, j, pos = 0, count = count0, new_count=0;
int is_closed = is_closed0;
bool le_eps = false;
size_t top = 0, stacksz = stack.size();
slice.start = slice.end = 0;
right_slice.start = right_slice.end = 0;
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;
if(le_eps)
slice_max_dist = max_dist;//记录划分失败的阈值
}
// 以最远2个点位分界, 划分为slice、right_slice两段
// 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);//如果最远距离都小于eps,则只存第一个点
}
// 递归划分slice:找出每个slice最远点,当距离>eps时,继续划分为2个slice,并入栈
// 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 ){
int 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 = ABS((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;
}
}
double gen_slice_dist = double(max_dist * max_dist) / (dx * dx + dy * dy);
le_eps = gen_slice_dist <= eps;
if(le_eps)
slice_max_dist = MAX(slice_max_dist, gen_slice_dist);//记录划分失败的阈值
//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这段被我删掉了
// last stage: do final clean-up of the approximated contour -
// remove extra points on the [almost] straight lines.
dst_contour.resize(new_count);
return sqrt(slice_max_dist);
}
//corner_pts:指定角点数量
void get_corner_pts(const std::vector<cv::Point>& src_contour, std::vector<cv::Point>& dst_contour, int corner_pts){
if(src_contour.size() <= 10)
return;
double last_eps_thred = opencv_approxPolyDP(src_contour, dst_contour, 1000, true);
while (dst_contour.size() < corner_pts) {
last_eps_thred = opencv_approxPolyDP(src_contour, dst_contour, last_eps_thred-1, true);
}
}
486

被折叠的 条评论
为什么被折叠?



