opencv轮廓提取指定数量角点

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);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值