Qt-OpenCV学习笔记--识别图形轮廓并计算图形坐标

概述

思路:将获得的图像进行修正,然后识别图形轮廓,最后计算图形坐标。

整体方案是基本函数和基本操作的综合应用。

此方案可以用于标签位置检测。

代码没有经过实际验证,此处仅是做个记录。

测试代码

myCV.pro

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    sm.cpp \
    widget.cpp

HEADERS += \
    sm.h \
    widget.h

FORMS += \
    widget.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

unix|win32: LIBS += -L$$PWD/../../../../../opencv/install/x64/mingw/lib/ -llibopencv_world454.dll

INCLUDEPATH += $$PWD/../../../../../opencv/install/include
DEPENDPATH += $$PWD/../../../../../opencv/install/include

sm.h

#ifndef SM_H
#define SM_H

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <iostream>

class sm
{
public:
    sm();

public:

    //预处理
    static cv::Mat pretreatment(cv::Mat src, int width, int height);
    //后处理
    static std::vector<cv::RotatedRect> postTreatment (cv::Mat src);
    //坐标排序
    static void sortPoints(cv::RotatedRect rect,cv::Point2f pts[]);
    //获取轮廓
    static std::vector<std::vector<cv::Point> > getContour(cv::Mat src, int threshold1, int threshold2);
    //裁切
    static cv::Mat cutBorder (cv::Mat src,int number);
};

#endif // SM_H

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_pushButton_clicked();

    void on_pushButton_2_clicked();

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H

main.cpp

#include "widget.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

sm.cpp

#include "sm.h"

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <QDebug>

sm::sm()
{

}

cv::Mat sm::pretreatment(cv::Mat src,int width, int height)
{
    //定义输出对象
    cv::Mat dst;

    //获得轮廓
    std::vector<std::vector<cv::Point>> contours;
    contours = sm::getContour(src,100,200);

    //先要判断所有图形是否全部内嵌
    if (contours.size()!=1)
    {
        qDebug()<<"图像加载错误,请重新加载!";
        return dst;
    }

    //获得矩形
    cv::RotatedRect box;
    box = cv::minAreaRect(contours[0]);
    qDebug()<<"矩形的角度:"<<box.angle;

    //获得矩形的顶点数组
    cv::Point2f pts[4];
    sm::sortPoints(box,pts);

    //校正
    double x = 2;
    double y = 1.5;

    pts[0].x = pts[0].x+x;
    pts[0].y = pts[0].y+y;
    pts[1].x = pts[1].x-x;
    pts[1].y = pts[1].y+y;
    pts[2].x = pts[2].x-x;
    pts[2].y = pts[2].y-y;
    pts[3].x = pts[3].x+x;
    pts[3].y = pts[3].y-y;

    //变换数组
    cv::Point2f pts_src[4];
    pts_src[0]=pts[0];
    pts_src[1]=pts[1];
    pts_src[2]=pts[2];
    pts_src[3]=pts[3];
    //对应数组
    cv::Point2f ps_dst[4];
    ps_dst[0] = cv::Point2f(0,0);
    ps_dst[1] = cv::Point2f(width,0);
    ps_dst[2] = cv::Point2f(width,height);
    ps_dst[3] = cv::Point2f(0,height);
    //变换矩阵
    cv::Mat M = getPerspectiveTransform(pts_src,ps_dst);
    //输出图像的大小
    cv::Size size(width,height);
    //透视变换
    warpPerspective(src,dst,M,size);
    //打印
    qDebug()<<"矩形的宽度:"<<dst.size().width;
    qDebug()<<"矩形的高度:"<<dst.size().height;

    qDebug()<<"图像预处理完成";

    //返回结果
    return dst;
}

std::vector<cv::RotatedRect> sm::postTreatment(cv::Mat src)
{
    //获得轮廓
    std::vector<std::vector<cv::Point>> contours;
    contours = sm::getContour(src,100,200);

    //定义输出对象
    std::vector<cv::RotatedRect> RotatedRect(contours.size());

    //获得矩形
    for (uint i=0;i<contours.size();i++)
    {
        RotatedRect[i] = minAreaRect(contours[i]);
    }

    qDebug()<<"后处理完成";

    //返回结果
    return RotatedRect;
}

void sm::sortPoints(cv::RotatedRect rect, cv::Point2f pts[])
{
    //获得中心点
    cv::Point2f p0;
    p0=rect.center;

    //获得顶点
    cv::Point2f p[4];
    rect.points(p);

    //排序
    for (int i= 0;i<4;i++)
    {
        if( p[i].x<p0.x && p[i].y<p0.y )
            pts[0]=p[i];
        else if( p[i].x>p0.x && p[i].y<p0.y )
            pts[1]=p[i];
        else if( p[i].x>p0.x && p[i].y>p0.y )
            pts[2]=p[i];
        else
            pts[3]=p[i];
    }

    //打印
    qDebug()<<"中心点:"<<p0.x<<"-"<<p0.y;
    qDebug()<<"第1个点:"<<pts[0].x<<"-"<<pts[0].y;
    qDebug()<<"第2个点:"<<pts[1].x<<"-"<<pts[1].y;
    qDebug()<<"第3个点:"<<pts[2].x<<"-"<<pts[2].y;
    qDebug()<<"第4个点:"<<pts[3].x<<"-"<<pts[3].y;

    qDebug()<<"点的排序完成";

}

std::vector<std::vector<cv::Point>> sm::getContour(cv::Mat src,int threshold1,int threshold2)
{
    //初步处理
    cv::Mat dst_gray,
            dst_blur,
            dst_canny,
            dst_dilate;
    //灰度处理
    cv::cvtColor(src,dst_gray,cv::COLOR_BGR2GRAY);
    //cv::imshow("dst_gray",dst_gray);
    //高斯模糊
    cv::GaussianBlur(dst_gray,dst_blur,cv::Size(3,3),0,0);
    //cv::imshow("dst_blur",dst_blur);
    //边缘检测
    cv::Canny(dst_blur,dst_canny,threshold1,threshold2);
    //cv::imshow("dst_canny",dst_canny);
    //膨胀
    cv::dilate(dst_canny,dst_dilate,cv::Mat());
    //cv::imshow("dst_dilate",dst_dilate);

    //检测轮廓
    std::vector<std::vector<cv::Point>> contours;
    findContours(dst_dilate,contours,cv::RETR_EXTERNAL,cv::CHAIN_APPROX_NONE);
    //drawContours(src,contours,-1,cv::Scalar(255,0,255),cv::FILLED);
    //cv::imshow("src",src);

    //打印轮廓数量
    qDebug()<<"轮廓数量:"<<contours.size();
    //打印轮廓坐标
    for (uint i=0;i<contours.size();i++)
    {
        for(uint j=0;j<contours[i].size();j++)
        {
            qDebug()<<"轮廓坐标:"<<contours[i][j].x<<"--"<<contours[i][j].y;
        }
    }

    qDebug()<<"获取轮廓完成";

    //返回结果
    return contours;
}

cv::Mat sm::cutBorder(cv::Mat src, int number)
{
    //定义输出对象
    cv::Mat dst;
    cv::Mat dst_temp;

    //定义裁切矩形
    cv::Point p_start(number,number);
    cv::Point p_end(src.size().width-number,src.size().height-number);
    cv::Rect rect(p_start,p_end);

    //裁切
    dst_temp=src(rect).clone();//这个地方是个坑!若不克隆,仅仅是局部显示,不进行真正的裁切!!!

    //缝补
    cv::copyMakeBorder(dst_temp,dst,number,number,number,number,cv::BORDER_REPLICATE);

    qDebug()<<"裁切完成";

    //返回结果
    return dst;
}


widget.cpp

#include "widget.h"
#include "ui_widget.h"

#include <QFileDialog>

#include <iostream>
#include <QDebug>

#include "sm.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
}

Widget::~Widget()
{
    delete ui;
}


void Widget::on_pushButton_clicked()
{
    //获取路径
    QString qstr = ui->lineEdit->text();
    //转换
    std::string str = qstr.toStdString();

    //加载图像
    std::string path =str;
    cv::Mat src = cv::imread(path);
    //判断是否加载成功
    if(src.empty())
    {
        qDebug()<<"加载图片失败!";
        return;
    }

    //图像预处理
    cv::Mat dst_p;
    //获取数据
    QString width = ui->lineEdit_2->text();
    QString height = ui->lineEdit_3->text();
    //qstring转int
    int width_i = width.toInt();
    int height_i = height.toInt();
    //预处理
    dst_p = sm::pretreatment(src,width_i,height_i);
    //判断预处理是否成功
    if(dst_p.empty())
    {
        qDebug()<<"图像预处理失败!";
        return;
    }

    //裁切
    cv::Mat dst_c;
    dst_c = sm::cutBorder(dst_p,20);

    //后处理
    std::vector<cv::RotatedRect> rects;
    rects = sm::postTreatment(dst_c);

    //打印结果
    std::vector<std::vector<cv::Point>> box(rects.size());

    for (uint i=0;i<rects.size();i++)
    {
        //打印坐标
        qDebug()<<"矩形"<<i<<":"<<rects[i].center.x;
        qDebug()<<"矩形"<<i<<":"<<rects[i].center.y;

        double a = rects[i].center.x;
        double b = rects[i].center.y;

        QString str1 = QString::number(a,'f',2);
        QString str2 = QString::number(b,'f',2);
        QString str3 = QString::number(i);

        QString str = "矩形"+str3+"的坐标是:"+str1+"--"+str2;

        ui->textBrowser->append(str);

        //获得矩形顶点
        cv::Point2f ps[4];
        rects[i].points(ps);
        box[i].push_back(ps[0]);
        box[i].push_back(ps[1]);
        box[i].push_back(ps[2]);
        box[i].push_back(ps[3]);

        //绘制矩形
        polylines(dst_c,box[i],1,cv::Scalar(0,255,0),2,8,0);

    }
    //打印空行
    ui->textBrowser->append(" ");
    //显示
    cv::imshow("dst_c",dst_c);
}

void Widget::on_pushButton_2_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this,"选择一个图片",".","*.jpg *.png *.bmp");
    if(!fileName.isEmpty())
    {
        ui->lineEdit->setText(fileName);
        ui->label_6->setPixmap(fileName);
    }

}

widget.ui

测试结果

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值