• Qt+opencv 鼠标画线实现几何图形识别并动态创建


    前言

    使用Qt + OpenCV实现,通过鼠标画线绘制几何图形,然后通过opencv进行图形轮廓识别,返回图形顶点,然后创建对应的几何图形添加到场景中。绘制使用QGraphics体系完成。

    看效果图:
    在这里插入图片描述


    本文demo在这里
    点击下载

    环境: Qt5.15.2 + vs2019 64bit


    支持图形:直线、圆、椭圆、矩形、三角形。
    快捷键:数字3 清屏

    正文

    demo的功能实现流程如下:

    在临时画线层绘制,然后将绘制的图形保存成一张临时图片,再将其传给opencv进行轮廓检测,返回轮廓点后再计算出轮廓顶点坐标,将坐标交给Qt层动态创建几何图形,添加到scene中。

    opencv下载

    本文中需要用到opencv的轮廓识别,所以先要准备好opencv的库,本文下载的是当前最新版本V4.6.0
    opencv下载地址

    在这里插入图片描述安装后,将其头文件和动态库拷贝到自己的工程项目中,并创建一个pri文件进行管理,也方便其他项目使用。

    在这里插入图片描述
    这里用到的动态库是opencv_world460.dll

    opencv.pri

    INCLUDEPATH += $$PWD/include
    
    win32 {
    CONFIG(release, debug|release) {
    LIBS += -L$$PWD/lib/ -lopencv_world460
    }
    
    CONFIG(debug, debug|release) {
    LIBS += -L$$PWD/lib/ -lopencv_world460d
    }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    OpenCV轮廓提取算法使用findContours()接口,详情可参考这里

    绘制

    本文使用QGraphics体系进行鼠标画线,是在之前的博客文章代码基础上复用的
    详情参考:

    Qt 鼠标/触屏绘制平滑曲线,支持矢量/非矢量方式
    Qt实现桌面画线、标记,流畅绘制,支持鼠标和多点触控绘制

    检测

    调用opencv的接口进行检测

    void ShapeDetecter::shapeDetect(string path_to_image)
    {
        RNG rng(123);
        // Read image
        Mat3b src = imread(path_to_image);
        // Convert to grayscale
        Mat1b gray;
        cvtColor(src, gray, COLOR_BGR2GRAY);
        // Binarize
        Mat1b bin;
        threshold(gray, bin, 175, 255, THRESH_OTSU|THRESH_BINARY_INV);
        // Perform thinning
        _thinning(bin, bin);
        // Create result image
    //    Mat3b res = src.clone();
        // Find contours
        vector<vector<Point>> contours;
        findContours(bin.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
        // For each contour
    
        if(contours.size() <=0)
            return;
        vector<Point> contour = contours[0];
        for (vector<Point>& contour : contours)
        {
            // Compute convex hull
            vector<Point> hull;
            convexHull(contour, hull);
    
            // Compute circularity, used for shape classification
            double area = contourArea(hull);
            double perimeter = arcLength(hull, true);
            double circularity = (4 * CV_PI * area) / (perimeter * perimeter);
            // Shape classification
            qDebug() << __FUNCTION__ << "circularity" << circularity;
    
            if(circularity > 0.85)
            {
                // circle
                  RotatedRect rect = fitEllipse(contour);
                 _drawCircle(rect.boundingRect());
    
            }
            else if(circularity > 0.68)
            {
                // Minimum oriented bounding box ...
                RotatedRect rect = minAreaRect(contour);
                Point2f pts[4];
                rect.points(pts);
    
                QVector<QPoint> points;
                for (int i = 0; i < 4; ++i)
                {
                   points.push_back(QPoint( pts[i].x,pts[i].y));
                }
    
                emit sigDrawPolygon(points);
    
            }
            else if (circularity > 0.5)
            {
                // TRIANGLE
                // Select the portion of the image containing only the wanted contour
                Rect roi = boundingRect(contour);
                Mat1b maskRoi(bin.rows, bin.cols, uchar(0));
                rectangle(maskRoi, roi, Scalar(255), FILLED);
                Mat1b triangle(roi.height, roi.height, uchar(0));
                bin.copyTo(triangle, maskRoi);
    
                // Find min encolsing circle on the contour
                Point2f center;
                float radius;
                minEnclosingCircle(contour, center, radius);
    
                // decrease the size of the enclosing circle until it intersects the contour
                // in at least 3 different points (i.e. the 3 vertices)
                vector<vector<Point>> vertices;
                do
                {
                    vertices.clear();
                    radius--;
    
                    Mat1b maskCirc(bin.rows, bin.cols, uchar(0));
                    circle(maskCirc, center, radius, Scalar(255), 5);
    
                    maskCirc &= triangle;
                    findContours(maskCirc.clone(), vertices, RETR_LIST, CHAIN_APPROX_NONE);
    
                } while (vertices.size() < 3);
    
                qDebug() << __FUNCTION__ <<"TRIANGLE "<< "vertices_size = " <<vertices.size();
                // Just get the first point in each vertex blob.
                // You could get the centroid for a little better accuracy
                QVector<QPoint> points;
                points.push_back(QPoint(vertices[0][0].x,vertices[0][0].y));
                points.push_back(QPoint(vertices[1][0].x,vertices[1][0].y));
                points.push_back(QPoint(vertices[2][0].x,vertices[2][0].y));
    //            emit sigDrawTriangle(points);
                emit sigDrawPolygon(points);
            }
            else
            {
               _drawLine(contours.at(0), boundingRect(contours.at(0)));
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    动态创建图形

    从opencv返回顶点接口后,这里直接快捷创建QGraphicsLineItemQGraphicsEllipseItemQGraphicsPolygonItem,也可以自定义QGraphicsItem 然后在paint中进行绘制,自由度更高,比如设置平滑及其他参数等。
    可以参考之前的博客
    Qt鼠标拖动绘制基本几何图形

    void WbCanvasItem::onDrawLine(const QPoint &point1, const QPoint &point2)
    {
        auto item = new QGraphicsLineItem(QLineF(point1,point2),this);
        item->setPen(QPen(Qt::red,5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
        m_pChildItems.append(item);
    }
    
    void WbCanvasItem::onDrawCircle(const QRect &rect)
    {
        auto item = new QGraphicsEllipseItem(rect,this);
        item->setPen(QPen(Qt::red,5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
        m_pChildItems.append(item);
    }
    
    void WbCanvasItem::onDrawPolygon(const QVector<QPoint> &pointVec)
    {
        auto item = new QGraphicsPolygonItem(QPolygonF(pointVec),this);
        item->setPen(QPen(Qt::red,5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
        m_pChildItems.append(item);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    本示例通过简单演示整个流程,若运用到实际项目中需要进一步优化。


    本文demo在这里
    点击下载

    环境: Qt5.15.2 + vs2019 64bit


  • 相关阅读:
    node爬虫爬取小说
    PHP毕业设计源代码剧影评|剧评影评系统
    处理一对多的映射关系
    eyb:RubbitMQ学习2
    在很多nlp数据集上超越tinybert 的新架构nlp神经网络模型
    SAP MIRO发票过账报错 发出数量为0
    量化投资 日周月报 2024-06-28
    网页轮播图
    解锁电力安全密码:迅软DSE助您保护机密无忧
    JPA中的乐观和悲观锁定
  • 原文地址:https://blog.csdn.net/luoyayun361/article/details/128170179