QT+Opencv计算并绘制轮廓的最小外接矩形

406 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

在处理的结果方面,总感觉使用c++的处理效果要略微好于使用python的处理效果,不知道是不是我的错觉。最近对python实现的内容,我又通过QT进行了实现,主要就是对检测到的轮廓进行标记,绘制最小外接矩形。

算法的代码如下:

// 轮廓中绘制最小矩形
// 这里的图像我是使用的前面图像增强后的
void Widget::process_draw_minrectangle(Mat img, String image_save_path, float font_size, int font_thickness, int line_thickness)
{
    Mat gray;
    Mat dstImg = img.clone();
    cvtColor(img, gray, COLOR_BGR2GRAY);
    Mat binary;
    // 自适应阈值分割
    adaptiveThreshold(gray, binary, 127, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 11, -30);
    imwrite("binary.jpg", binary);
    Mat erode_img;
    // 腐蚀
    erode(binary, erode_img, 5);
    imwrite("erode.jpg", erode_img);
    // 建立结构保存寻找轮廓得到的数据点
    std::vector<std::vector<Point> > contours;
    std::vector<Vec4i> hierarchy;
    findContours(binary, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    int count = 0;
    vector<Rect> boundRect(contours.size());
    vector<RotatedRect> box(contours.size());
    Point2f rect[4];
    // 对每一个轮廓进行遍历
    for(int i=0;i<contours.size();i++)
    {
        box[i] = minAreaRect(Mat(contours[i]));
        boundRect[i] = boundingRect((Mat(contours[i])));
        circle(dstImg, Point(box[i].center.x, box[i].center.y), 5, Scalar(0, 255, 0), -1, 8);
        box[i].points(rect);  //把最小外接矩形四个端点复制给rect数组
        //rectangle(dstImg, Point(boundRect[i].x, boundRect[i].y), Point(boundRect[i].x + boundRect[i].width, boundRect[i].y + boundRect[i].height), Scalar(0, 255, 0), 2, 8);
        for(int j=0; j<4; j++)
        {
            // 绘制最小外接矩形的边
            line(dstImg, rect[j], rect[(j+1)%4], Scalar(0, 0, 255), 2, 8);
        }
    }
    imshow("dst", dstImg);
    waitKey(0);
}

上述算法主要作用就是通过对最小轮廓的查找,记录轮廓的坐标点,然后利用绘制每条线段的方式找到最小外接矩形,算法的效率比较高,检测的事件在毫秒级。

下面是对一些实验结果的分析

image.png

上面是对轮廓的检测和绘制结果,在绘制矩形时同时计算出了其几何中心,也一并绘制出,所以看到的图像中存在一些绿色的圆点,即为整个矩形的中心点。

在上述代码中只需要注释掉circle一行即可不绘制图像的几何中心点,如下面所示。 image.png

同时,还可以在绘制时计算一下轮廓面积,去除较小可以忽略的轮廓,同时可以使用NMS的思想去除掉被大框圈住的小框等。