概述
思路:将获得的图像进行修正,然后识别图形轮廓,最后计算图形坐标。
整体方案是基本函数和基本操作的综合应用。
此方案可以用于标签位置检测。
代码没有经过实际验证,此处仅是做个记录。
测试代码
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
测试结果