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

本文demo在这里
点击下载
环境: Qt5.15.2 + vs2019 64bit
支持图形:直线、圆、椭圆、矩形、三角形。
快捷键:数字3 清屏
demo的功能实现流程如下:
在临时画线层绘制,然后将绘制的图形保存成一张临时图片,再将其传给opencv进行轮廓检测,返回轮廓点后再计算出轮廓顶点坐标,将坐标交给Qt层动态创建几何图形,添加到scene中。
本文中需要用到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
}
}
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)));
}
}
}
从opencv返回顶点接口后,这里直接快捷创建QGraphicsLineItem、QGraphicsEllipseItem、QGraphicsPolygonItem,也可以自定义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);
}
本示例通过简单演示整个流程,若运用到实际项目中需要进一步优化。
本文demo在这里
点击下载
环境: Qt5.15.2 + vs2019 64bit
出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在
如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法
我正在尝试按0-9和a-z的顺序创建数字和字母列表。我有一组值value_array=['0','1','2','3','4','5','6','7','8','9','a','b','光盘','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','','u','v','w','x','y','z']和一个组合列表的数组,按顺序,这些数字可以产生x个字符,比方说三个list_array=[]和一个当前字母和数字组合的数组(在将它插入列表数组之前我会把它变成一个字符串,]current_combo['0','0','0']
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD