opencv实现轮廓的内接正矩形

参考python-opencv 图像捕捉多个不规则轮廓,与轮廓内接区域(圆/矩形)思路-持续更新编辑中(会附上详细的思路解释和图片) - Lorzen - 博客园


前言

提示:利用中心延展算法的思想,加入快扩散慢收缩,加快运行速度。由于使用矩来求轮廓的重心,并作为扩散的中心点,所以对u形的区域不友好。


以下是本篇文章正文内容,下面案例可供参考

一、源码

代码如下(示例):

//最大内接矩形,传入单个轮廓
//利用中心延展算法的思想,加入快扩散慢收缩。加快运行速度
bool MaxInnerRect(const vector &con, Rect &R) {
    Moments moment = moments(con);
    //由于使用矩来求轮廓的重心,并作为扩散的中心点,所以对u形的区域不友好,
    Point midPoint = Point(int(moment.m10 / moment.m00), int(moment.m01 / moment.m00));
    double Flag = pointPolygonTest(con, midPoint, false);
    if (Flag <= 0) {
        R.x = 0;
        R.y = 0;
        R.width = 0;
        R.height = 0;
        return false;
    }
    Rect rect = boundingRect(con);
    int x1, x2, y1, y2;//中心扩散的四个顶点坐标
    x1 = midPoint.x - 1;
    x2 = midPoint.x + 1;
    y1 = midPoint.y - 1;
    y2 = midPoint.y + 1;
//*********//
    int cnt_range, step, radio_x, radio_y;
    int thre = 4;//由于轮廓并非是很直的线,一个突出点会影响结果,所以要设置敏感值,太小结果不对
    
    if (rect.width > rect.height) {// 判断轮廓 X方向更长
        cnt_range = rect.width;    //获得长边
    }
    else {
        cnt_range = rect.height;
    }
    bool flag_x1 = false, flag_x2 = false, flag_y1 = false, flag_y2 = false;//定义标志,标记是否到达边缘
    radio_x = rect.width / 6;//根据区域的外接矩形边来决定x边扩散的步长
    radio_y = rect.height / 6;//根据区域的外接矩形边来决定y边扩散的步长
    step = cnt_range/120;//
    if (step<1) {   //收缩时步长等于1;step代表每次收缩时,判断点在轮廓外面的步长,必须大于等于1,同时也不能太大,会影响内接矩阵的精度
        step = 1;
    }
    if (step>=3) {
        step = 2;
    }
    //四条边分别求,每条边找到第一次出现在轮廓外的点,然后再收缩,直到找到边界
    //(此处为了让边不要太敏感,设置出现在轮廓外的点大于thre时,才开始收缩)
    for (int ix = 1; ix < cnt_range; ++ix) {
        if (flag_y1 == false) {
            y1 = y1 - radio_y;//每次扩散radio_y行
            int numy1 = 0;
            for (int i = x1; i <= x2;i=i+ step) {
                double p_x1y1 = pointPolygonTest(con, Point(i, y1), false);
                if (p_x1y1 < 0) {
                    numy1++;
                }
                if (numy1 > thre || y1 <= rect.y) {//开始收缩
                    for (int m = 0; m < radio_y -1 ; ++m) {
                        int numy11 = 0;
                        y1 = y1+ 1;     // 每次收缩1行
                        for (int j = x1; j <= x2; j=j+ step) {
                            double p_jy1 = pointPolygonTest(con, Point(j, y1), false);
                            if (p_jy1 < 0) {
                                numy11++;
                            }
                            if (numy11 > thre)
                                break;//继续收缩
                        }
                        if (numy11 > thre)
                            continue;//继续收缩
                        else
                            break;//找到结果收缩结束
                    }
                    flag_y1 = true;
                    break;//找到结果收缩结束
                }
            }
        }
        if (flag_x1 == false) {
            x1 = x1 - radio_x;
            int numx1 = 0;
            for (int i = y1; i <= y2; i = i + step) {
                double p_x1i = pointPolygonTest(con, Point(x1, i), false);
                if (p_x1i < 0) {
                    numx1++;
                }
                if (numx1 > thre || x1<= rect.x) {//开始收缩
                    for (int m = 0; m < radio_x - 1; ++m) {
                        int numx11 = 0;
                        x1 = x1 + 1;    //每次收缩1行
                        for (int j = y1; j <= y2; j = j + step) {
                            double p_x1j = pointPolygonTest(con, Point(x1, j), false);
                            if (p_x1j < 0) {
                                numx11++;
                            }
                            if (numx11 > thre)
                                break;
                        }
                        if (numx11 > thre)
                            continue;
                        else
                            break;
                    }
                    flag_x1 = true;
                    break;
                }
            }
        }

        if (flag_y2 == false) {
            y2 = y2 + radio_y;
            int numy2 = 0;
            for (int i = x1; i <= x2; i =i + step) {
                double p_iy2 = pointPolygonTest(con, Point(i, y2), false);
                if (p_iy2 < 0) {
                    numy2++;
                }
                if (numy2 > thre || y2 >= (rect.y+rect.height)) {开始收缩
                    for (int m = 0; m < radio_y - 1; ++m) {
                        int numy22 = 0;
                        y2 = y2 - 1;      //  每次收缩1行
                        for (int j = x1; j <= x2; j = j + step) {
                            double p_jy2 = pointPolygonTest(con, Point(j, y2), false);
                            if (p_jy2 < 0) {
                                numy22++;
                            }
                            if (numy22 > thre)
                                break;
                        }
                        if (numy22 > thre)
                            continue;
                        else
                            break;
                    }
                    flag_y2 = true;
                    break;
                }
            }
        }

        if (flag_x2 == false) {
            x2 = x2 + radio_x;
            int numx2 = 0;
            for (int i = y1; i <= y2; i = i + step) {
                double p_x2i = pointPolygonTest(con, Point(x2, i), false);
                if (p_x2i < 0) {
                    numx2++;
                }
                if (numx2 > thre || x2 >= (rect.x + rect.width)) {//开始收缩
                    for (int m = 0; m < radio_x - 1; ++m) {
                        int numx22 = 0;
                        x2 = x2 - 1;    // 每次收缩1行
                        for (int j = y1; j <= y2; j=j+ step) {
                            double p_x2j = pointPolygonTest(con, Point(x2, j), false);
                            if (p_x2j < 0) {
                                numx22++;
                            }
                            if (numx22 > thre)
                                break;
                        }
                        if (numx22 > thre)
                            continue;
                        else
                            break;
                    }
                    flag_x2 = true;
                    break;
                }
            }
        }
        if (flag_y1 && flag_x1 && flag_y2 && flag_x2) {
            break;
        }
    }
    R.x = x1;
    R.y = y1;
    R.width = x2 - x1;
    R.height = y2 - y1;
    return true;
}
//最大内接矩形重载,传入多个轮廓
bool MaxInnerRect(const vector> &cons, vector &R) {
    Rect r;
    if (cons.size() == 0) {
        return false;
    }
    for (int i = 0; i < cons.size(); i++) {
        if (MaxInnerRect(cons[i], r)) {
            R.push_back(r);
        }
    }
    if (R.size() == 0) {
        return false;
    }
    return true;
}

2.测试

代码如下(示例):

void testInnerRect(int flag) {
    Mat img = imread("1.png", 1);
    Mat src;
    cvtColor(img, src, COLOR_BGR2GRAY);
    threshold(src, src, 130, 255, THRESH_BINARY);//
    vector> contours, SelectContours;

    findContours(src, contours, RETR_LIST, CHAIN_APPROX_NONE);
    int jpg = 10000000;
    int png = 3000;
    clock_t begin = clock();
    if (flag == 1) {
        vector R;
        for (int i = 0; i < contours.size(); i = i + 1) {
            double area1 = contourArea(contours[i]);
            if (area1 > png && area1< 500000) {
                SelectContours.push_back(contours[i]);
            }
        }
        if (MaxInnerRect(SelectContours, R)) {
            for (int m = 0; m < R.size();m++) {
                rectangle(img, R[m], Scalar(0, 0, 255), 3);
            }

        }
    
    }
    else {
        for (int i = 0; i < contours.size(); i = i + 1) {//                                                    
            double area1 = contourArea(contours[i]);
            if (area1 > jpg) {
                SelectContours.push_back(contours[i]);
            }
        }
        vector cons1;
        Rect R;
        for (int i = 0; i < SelectContours.size(); i++) {
            cons1 = SelectContours[i];
            if (MaxInnerRect(cons1, R)) {
                rectangle(img, R, Scalar(0, 0, 255), 3);
            }
        }
    }
    clock_t end = clock();
    double programTimes = ((double)end - begin) / CLOCKS_PER_SEC;
    cout << "执行时间: " << programTimes << endl;
    namedWindow("img", WINDOW_NORMAL);
    namedWindow("src", WINDOW_NORMAL);
    imshow("img", img);
    imshow("src", src);
    waitKey(0);
}

总结

有改善之处还望提出,共勉!

你可能感兴趣的:(opencv,c++,计算机视觉)