Qt+opencv 鼠标画线实现几何图形识别并动态创建
创始人
2024-03-17 09:53:17
0

前言

使用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/includewin32 {
CONFIG(release, debug|release) {
LIBS += -L$$PWD/lib/ -lopencv_world460
}CONFIG(debug, debug|release) {
LIBS += -L$$PWD/lib/ -lopencv_world460d
}
}

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

绘制

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

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

检测

调用opencv的接口进行检测

void ShapeDetecter::shapeDetect(string path_to_image)
{RNG rng(123);// Read imageMat3b src = imread(path_to_image);// Convert to grayscaleMat1b gray;cvtColor(src, gray, COLOR_BGR2GRAY);// BinarizeMat1b bin;threshold(gray, bin, 175, 255, THRESH_OTSU|THRESH_BINARY_INV);// Perform thinning_thinning(bin, bin);// Create result image
//    Mat3b res = src.clone();// Find contoursvector> contours;findContours(bin.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);// For each contourif(contours.size() <=0)return;vector contour = contours[0];for (vector& contour : contours){// Compute convex hullvector hull;convexHull(contour, hull);// Compute circularity, used for shape classificationdouble area = contourArea(hull);double perimeter = arcLength(hull, true);double circularity = (4 * CV_PI * area) / (perimeter * perimeter);// Shape classificationqDebug() << __FUNCTION__ << "circularity" << circularity;if(circularity > 0.85){// circleRotatedRect 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 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 contourRect 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 contourPoint2f 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> 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 = " < 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)));}}
}

动态创建图形

从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 &pointVec)
{auto item = new QGraphicsPolygonItem(QPolygonF(pointVec),this);item->setPen(QPen(Qt::red,5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));m_pChildItems.append(item);
}

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


本文demo在这里
点击下载

环境: Qt5.15.2 + vs2019 64bit


相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...