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


    概述

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

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

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

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

    测试代码

    myCV.pro

    1. QT += core gui
    2. greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    3. CONFIG += c++11
    4. # You can make your code fail to compile if it uses deprecated APIs.
    5. # In order to do so, uncomment the following line.
    6. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
    7. SOURCES += \
    8. main.cpp \
    9. sm.cpp \
    10. widget.cpp
    11. HEADERS += \
    12. sm.h \
    13. widget.h
    14. FORMS += \
    15. widget.ui
    16. # Default rules for deployment.
    17. qnx: target.path = /tmp/$${TARGET}/bin
    18. else: unix:!android: target.path = /opt/$${TARGET}/bin
    19. !isEmpty(target.path): INSTALLS += target
    20. unix|win32: LIBS += -L$$PWD/../../../../../opencv/install/x64/mingw/lib/ -llibopencv_world454.dll
    21. INCLUDEPATH += $$PWD/../../../../../opencv/install/include
    22. DEPENDPATH += $$PWD/../../../../../opencv/install/include

    sm.h

    1. #ifndef SM_H
    2. #define SM_H
    3. #include <opencv2/core/core.hpp>
    4. #include <opencv2/highgui/highgui.hpp>
    5. #include <opencv2/imgproc/imgproc.hpp>
    6. #include <iostream>
    7. class sm
    8. {
    9. public:
    10. sm();
    11. public:
    12. //预处理
    13. static cv::Mat pretreatment(cv::Mat src, int width, int height);
    14. //后处理
    15. static std::vector<cv::RotatedRect> postTreatment (cv::Mat src);
    16. //坐标排序
    17. static void sortPoints(cv::RotatedRect rect,cv::Point2f pts[]);
    18. //获取轮廓
    19. static std::vector<std::vector<cv::Point> > getContour(cv::Mat src, int threshold1, int threshold2);
    20. //裁切
    21. static cv::Mat cutBorder (cv::Mat src,int number);
    22. };
    23. #endif // SM_H

    widget.h

    1. #ifndef WIDGET_H
    2. #define WIDGET_H
    3. #include
    4. QT_BEGIN_NAMESPACE
    5. namespace Ui { class Widget; }
    6. QT_END_NAMESPACE
    7. class Widget : public QWidget
    8. {
    9. Q_OBJECT
    10. public:
    11. Widget(QWidget *parent = nullptr);
    12. ~Widget();
    13. private slots:
    14. void on_pushButton_clicked();
    15. void on_pushButton_2_clicked();
    16. private:
    17. Ui::Widget *ui;
    18. };
    19. #endif // WIDGET_H

    main.cpp

    1. #include "widget.h"
    2. #include
    3. int main(int argc, char *argv[])
    4. {
    5. QApplication a(argc, argv);
    6. Widget w;
    7. w.show();
    8. return a.exec();
    9. }

    sm.cpp

    1. #include "sm.h"
    2. #include <opencv2/core/core.hpp>
    3. #include <opencv2/highgui/highgui.hpp>
    4. #include <opencv2/imgproc/imgproc.hpp>
    5. #include <QDebug>
    6. sm::sm()
    7. {
    8. }
    9. cv::Mat sm::pretreatment(cv::Mat src,int width, int height)
    10. {
    11. //定义输出对象
    12. cv::Mat dst;
    13. //获得轮廓
    14. std::vector<std::vector<cv::Point>> contours;
    15. contours = sm::getContour(src,100,200);
    16. //先要判断所有图形是否全部内嵌
    17. if (contours.size()!=1)
    18. {
    19. qDebug()<<"图像加载错误,请重新加载!";
    20. return dst;
    21. }
    22. //获得矩形
    23. cv::RotatedRect box;
    24. box = cv::minAreaRect(contours[0]);
    25. qDebug()<<"矩形的角度:"<<box.angle;
    26. //获得矩形的顶点数组
    27. cv::Point2f pts[4];
    28. sm::sortPoints(box,pts);
    29. //校正
    30. double x = 2;
    31. double y = 1.5;
    32. pts[0].x = pts[0].x+x;
    33. pts[0].y = pts[0].y+y;
    34. pts[1].x = pts[1].x-x;
    35. pts[1].y = pts[1].y+y;
    36. pts[2].x = pts[2].x-x;
    37. pts[2].y = pts[2].y-y;
    38. pts[3].x = pts[3].x+x;
    39. pts[3].y = pts[3].y-y;
    40. //变换数组
    41. cv::Point2f pts_src[4];
    42. pts_src[0]=pts[0];
    43. pts_src[1]=pts[1];
    44. pts_src[2]=pts[2];
    45. pts_src[3]=pts[3];
    46. //对应数组
    47. cv::Point2f ps_dst[4];
    48. ps_dst[0] = cv::Point2f(0,0);
    49. ps_dst[1] = cv::Point2f(width,0);
    50. ps_dst[2] = cv::Point2f(width,height);
    51. ps_dst[3] = cv::Point2f(0,height);
    52. //变换矩阵
    53. cv::Mat M = getPerspectiveTransform(pts_src,ps_dst);
    54. //输出图像的大小
    55. cv::Size size(width,height);
    56. //透视变换
    57. warpPerspective(src,dst,M,size);
    58. //打印
    59. qDebug()<<"矩形的宽度:"<<dst.size().width;
    60. qDebug()<<"矩形的高度:"<<dst.size().height;
    61. qDebug()<<"图像预处理完成";
    62. //返回结果
    63. return dst;
    64. }
    65. std::vector<cv::RotatedRect> sm::postTreatment(cv::Mat src)
    66. {
    67. //获得轮廓
    68. std::vector<std::vector<cv::Point>> contours;
    69. contours = sm::getContour(src,100,200);
    70. //定义输出对象
    71. std::vector<cv::RotatedRect> RotatedRect(contours.size());
    72. //获得矩形
    73. for (uint i=0;i<contours.size();i++)
    74. {
    75. RotatedRect[i] = minAreaRect(contours[i]);
    76. }
    77. qDebug()<<"后处理完成";
    78. //返回结果
    79. return RotatedRect;
    80. }
    81. void sm::sortPoints(cv::RotatedRect rect, cv::Point2f pts[])
    82. {
    83. //获得中心点
    84. cv::Point2f p0;
    85. p0=rect.center;
    86. //获得顶点
    87. cv::Point2f p[4];
    88. rect.points(p);
    89. //排序
    90. for (int i= 0;i<4;i++)
    91. {
    92. if( p[i].x<p0.x && p[i].y<p0.y )
    93. pts[0]=p[i];
    94. else if( p[i].x>p0.x && p[i].y<p0.y )
    95. pts[1]=p[i];
    96. else if( p[i].x>p0.x && p[i].y>p0.y )
    97. pts[2]=p[i];
    98. else
    99. pts[3]=p[i];
    100. }
    101. //打印
    102. qDebug()<<"中心点:"<<p0.x<<"-"<<p0.y;
    103. qDebug()<<"第1个点:"<<pts[0].x<<"-"<<pts[0].y;
    104. qDebug()<<"第2个点:"<<pts[1].x<<"-"<<pts[1].y;
    105. qDebug()<<"第3个点:"<<pts[2].x<<"-"<<pts[2].y;
    106. qDebug()<<"第4个点:"<<pts[3].x<<"-"<<pts[3].y;
    107. qDebug()<<"点的排序完成";
    108. }
    109. std::vector<std::vector<cv::Point>> sm::getContour(cv::Mat src,int threshold1,int threshold2)
    110. {
    111. //初步处理
    112. cv::Mat dst_gray,
    113. dst_blur,
    114. dst_canny,
    115. dst_dilate;
    116. //灰度处理
    117. cv::cvtColor(src,dst_gray,cv::COLOR_BGR2GRAY);
    118. //cv::imshow("dst_gray",dst_gray);
    119. //高斯模糊
    120. cv::GaussianBlur(dst_gray,dst_blur,cv::Size(3,3),0,0);
    121. //cv::imshow("dst_blur",dst_blur);
    122. //边缘检测
    123. cv::Canny(dst_blur,dst_canny,threshold1,threshold2);
    124. //cv::imshow("dst_canny",dst_canny);
    125. //膨胀
    126. cv::dilate(dst_canny,dst_dilate,cv::Mat());
    127. //cv::imshow("dst_dilate",dst_dilate);
    128. //检测轮廓
    129. std::vector<std::vector<cv::Point>> contours;
    130. findContours(dst_dilate,contours,cv::RETR_EXTERNAL,cv::CHAIN_APPROX_NONE);
    131. //drawContours(src,contours,-1,cv::Scalar(255,0,255),cv::FILLED);
    132. //cv::imshow("src",src);
    133. //打印轮廓数量
    134. qDebug()<<"轮廓数量:"<<contours.size();
    135. //打印轮廓坐标
    136. for (uint i=0;i<contours.size();i++)
    137. {
    138. for(uint j=0;j<contours[i].size();j++)
    139. {
    140. qDebug()<<"轮廓坐标:"<<contours[i][j].x<<"--"<<contours[i][j].y;
    141. }
    142. }
    143. qDebug()<<"获取轮廓完成";
    144. //返回结果
    145. return contours;
    146. }
    147. cv::Mat sm::cutBorder(cv::Mat src, int number)
    148. {
    149. //定义输出对象
    150. cv::Mat dst;
    151. cv::Mat dst_temp;
    152. //定义裁切矩形
    153. cv::Point p_start(number,number);
    154. cv::Point p_end(src.size().width-number,src.size().height-number);
    155. cv::Rect rect(p_start,p_end);
    156. //裁切
    157. dst_temp=src(rect).clone();//这个地方是个坑!若不克隆,仅仅是局部显示,不进行真正的裁切!!!
    158. //缝补
    159. cv::copyMakeBorder(dst_temp,dst,number,number,number,number,cv::BORDER_REPLICATE);
    160. qDebug()<<"裁切完成";
    161. //返回结果
    162. return dst;
    163. }

    widget.cpp

    1. #include "widget.h"
    2. #include "ui_widget.h"
    3. #include <QFileDialog>
    4. #include <iostream>
    5. #include <QDebug>
    6. #include "sm.h"
    7. Widget::Widget(QWidget *parent)
    8. : QWidget(parent)
    9. , ui(new Ui::Widget)
    10. {
    11. ui->setupUi(this);
    12. }
    13. Widget::~Widget()
    14. {
    15. delete ui;
    16. }
    17. void Widget::on_pushButton_clicked()
    18. {
    19. //获取路径
    20. QString qstr = ui->lineEdit->text();
    21. //转换
    22. std::string str = qstr.toStdString();
    23. //加载图像
    24. std::string path =str;
    25. cv::Mat src = cv::imread(path);
    26. //判断是否加载成功
    27. if(src.empty())
    28. {
    29. qDebug()<<"加载图片失败!";
    30. return;
    31. }
    32. //图像预处理
    33. cv::Mat dst_p;
    34. //获取数据
    35. QString width = ui->lineEdit_2->text();
    36. QString height = ui->lineEdit_3->text();
    37. //qstring转int
    38. int width_i = width.toInt();
    39. int height_i = height.toInt();
    40. //预处理
    41. dst_p = sm::pretreatment(src,width_i,height_i);
    42. //判断预处理是否成功
    43. if(dst_p.empty())
    44. {
    45. qDebug()<<"图像预处理失败!";
    46. return;
    47. }
    48. //裁切
    49. cv::Mat dst_c;
    50. dst_c = sm::cutBorder(dst_p,20);
    51. //后处理
    52. std::vector<cv::RotatedRect> rects;
    53. rects = sm::postTreatment(dst_c);
    54. //打印结果
    55. std::vector<std::vector<cv::Point>> box(rects.size());
    56. for (uint i=0;i<rects.size();i++)
    57. {
    58. //打印坐标
    59. qDebug()<<"矩形"<<i<<":"<<rects[i].center.x;
    60. qDebug()<<"矩形"<<i<<":"<<rects[i].center.y;
    61. double a = rects[i].center.x;
    62. double b = rects[i].center.y;
    63. QString str1 = QString::number(a,'f',2);
    64. QString str2 = QString::number(b,'f',2);
    65. QString str3 = QString::number(i);
    66. QString str = "矩形"+str3+"的坐标是:"+str1+"--"+str2;
    67. ui->textBrowser->append(str);
    68. //获得矩形顶点
    69. cv::Point2f ps[4];
    70. rects[i].points(ps);
    71. box[i].push_back(ps[0]);
    72. box[i].push_back(ps[1]);
    73. box[i].push_back(ps[2]);
    74. box[i].push_back(ps[3]);
    75. //绘制矩形
    76. polylines(dst_c,box[i],1,cv::Scalar(0,255,0),2,8,0);
    77. }
    78. //打印空行
    79. ui->textBrowser->append(" ");
    80. //显示
    81. cv::imshow("dst_c",dst_c);
    82. }
    83. void Widget::on_pushButton_2_clicked()
    84. {
    85. QString fileName = QFileDialog::getOpenFileName(this,"选择一个图片",".","*.jpg *.png *.bmp");
    86. if(!fileName.isEmpty())
    87. {
    88. ui->lineEdit->setText(fileName);
    89. ui->label_6->setPixmap(fileName);
    90. }
    91. }

    widget.ui

    测试结果

     

  • 相关阅读:
    Salesforce LWC学习(四十七) 标准页面更新以后自定义页面如何捕捉?
    数据结构篇——KMP算法
    C语言题收录(六)
    顶礼膜拜!阿里内部出品,全网首发 Spring Security 项目实战搭建
    数据可视化:随时间变化的效果图
    数字孪生技术解决方案助力智慧核电建设
    maven-依赖管理
    机器学习:过拟合、欠拟合、正则化之间的纸短情长~
    windows下VIM配置c/c++自动补全代码
    线程池_概述_拒绝策略等
  • 原文地址:https://blog.csdn.net/ssismm/article/details/128016901