前言
使用Qt + OpenCV實(shí)現(xiàn),通過鼠標(biāo)畫線繪制幾何圖形,然后通過opencv進(jìn)行圖形輪廓識別,返回圖形頂點(diǎn),然后創(chuàng)建對應(yīng)的幾何圖形添加到場景中。繪制使用QGraphics體系完成。
看效果圖:
本文demo在這里
點(diǎn)擊下載文章來源:http://www.zghlxwxcb.cn/news/detail-496834.html
環(huán)境: Qt5.15.2 + vs2019 64bit文章來源地址http://www.zghlxwxcb.cn/news/detail-496834.html
支持圖形:直線、圓、橢圓、矩形、三角形。
快捷鍵:數(shù)字3 清屏
正文
demo的功能實(shí)現(xiàn)流程如下:
在臨時畫線層繪制,然后將繪制的圖形保存成一張臨時圖片,再將其傳給opencv進(jìn)行輪廓檢測,返回輪廓點(diǎn)后再計算出輪廓頂點(diǎn)坐標(biāo),將坐標(biāo)交給Qt層動態(tài)創(chuàng)建幾何圖形,添加到scene中。
opencv下載
本文中需要用到opencv的輪廓識別,所以先要準(zhǔn)備好opencv的庫,本文下載的是當(dāng)前最新版本V4.6.0
opencv下載地址
安裝后,將其頭文件和動態(tài)庫拷貝到自己的工程項目中,并創(chuàng)建一個pri文件進(jìn)行管理,也方便其他項目使用。
這里用到的動態(tài)庫是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體系進(jìn)行鼠標(biāo)畫線,是在之前的博客文章代碼基礎(chǔ)上復(fù)用的
詳情參考:
Qt 鼠標(biāo)/觸屏繪制平滑曲線,支持矢量/非矢量方式
Qt實(shí)現(xiàn)桌面畫線、標(biāo)記,流暢繪制,支持鼠標(biāo)和多點(diǎn)觸控繪制
檢測
調(diào)用opencv的接口進(jìn)行檢測
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)));
}
}
}
動態(tài)創(chuàng)建圖形
從opencv返回頂點(diǎn)接口后,這里直接快捷創(chuàng)建QGraphicsLineItem
、QGraphicsEllipseItem
、QGraphicsPolygonItem
,也可以自定義QGraphicsItem 然后在paint中進(jìn)行繪制,自由度更高,比如設(shè)置平滑及其他參數(shù)等。
可以參考之前的博客
Qt鼠標(biāo)拖動繪制基本幾何圖形
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);
}
本示例通過簡單演示整個流程,若運(yùn)用到實(shí)際項目中需要進(jìn)一步優(yōu)化。
本文demo在這里
點(diǎn)擊下載
環(huán)境: Qt5.15.2 + vs2019 64bit
到了這里,關(guān)于Qt+opencv 鼠標(biāo)畫線實(shí)現(xiàn)幾何圖形識別并動態(tài)創(chuàng)建的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!