Qt可以在自带的界面编辑器Qt Creator下编写,也可以在VS下配置Qt 环境编写,本文将介绍在Creator下编写一个简单的界面用以实现图片拼接的功能。
图片拼接需要使用到opencv库,所以需要在Qt Creator 下配置一下opencv,此步骤可百度教程即可,本文就不再赘述,现给出pro文件内的环境配置代码(本文以2.4.13版本为例)。
INCLUDEPATH+=D:\opencv2413\opencv\build\include\
D:\opencv2413\opencv\build\include\opencv\
D:\opencv2413\opencv\build\include\opencv2
LIBS +=-LD:\opencv2413\opencv\build\x86\vc11\lib \
-lopencv_ml2413d\
-lopencv_calib3d2413d\
-lopencv_contrib2413d\
-lopencv_core2413d\
-lopencv_features2d2413d\
-lopencv_flann2413d\
-lopencv_gpu2413d\
-lopencv_highgui2413d\
-lopencv_imgproc2413d\
-lopencv_legacy2413d\
-lopencv_objdetect2413d\
-lopencv_ts2413d\
-lopencv_video2413d\
-lopencv_nonfree2413d\
-lopencv_ocl2413d\
-lopencv_photo2413d\
-lopencv_stitching2413d\
-lopencv_superres2413d\
-lopencv_videostab2413d
在环境配置完成后,开始ui设计,本次设计界面如下,在File下拉框内添加Open打开动作用以打开图片。
在Creator下的控件关联比较方便,可以不需要使用connect函数,直接“转到槽”即可。
先打开两张图片,并显示在label1、label2控件上,然后实现点击按钮进行拼接,并将拼接结果图片显示在label3控件上。
代码实现如下:
//头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
Mat image01,image02;
QImage img01,img02,imgpano;
typedef struct
{
Point2f left_top;
Point2f left_bottom;
Point2f right_top;
Point2f right_bottom;
}four_corners_t;
four_corners_t corners;
void paintEvent(QPaintEvent *);
void MainWindow::OptimizeSeam(Mat& img1, Mat& trans, Mat& dst);
void MainWindow::CalcCorners(const Mat& H, const Mat& src);
private slots:
void on_actionOpen_triggered();
void on_pushButton_stitch_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
//源文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
//设置背景图
void MainWindow::paintEvent(QPaintEvent *)
{
QPainter p(this);
//画背景图
p.drawPixmap(0,0,width(),height(),QPixmap("D:/lena.jpg"));
}
void MainWindow::on_actionOpen_triggered()
{
//从路径中导入第一张图片
QString fileName = QFileDialog::getOpenFileName(this,"Open Image",
"../","Image Files (*.png *.jpg *.bmp)");
QTextCodec *code = QTextCodec::codecForName("GB2312");
string name = code->fromUnicode(fileName).data();
image01=imread(name,1);
cvtColor(image01,image01,CV_BGR2RGB);
//将Mat转为QImage
img01 = QImage((const unsigned char*)(image01.data),
image01.cols,image01.rows,image01.cols*image01.channels(),
QImage::Format_RGB888);
ui->label->clear();
//将图片以Label控件大小显示
img01= img01.scaled(ui->label->width(),ui->label->height());
ui->label->setPixmap(QPixmap::fromImage(img01));
//自定义路径中导入第二张图片
QString fileName2 = QFileDialog::getOpenFileName(this,"Open Image",
"../","Image Files (*.png *.jpg *.bmp)");
QTextCodec *code2 = QTextCodec::codecForName("GB2312");
string name2 = code2->fromUnicode(fileName2).data();
image02=imread(name2,1);
//下面的操作主要为格式的转换
cvtColor(image02,image02,CV_BGR2RGB);//保证颜色通道
img02 = QImage((const unsigned char*)(image02.data),
image02.cols,image02.rows,image02.cols*image02.channels(),
QImage::Format_RGB888);
ui->label_right->clear();
img02= img02.scaled(ui->label_right->width(),ui->label_right->height());
ui->label_right->setPixmap(QPixmap::fromImage(img02));
}
void MainWindow::on_pushButton_stitch_clicked()
{
Mat image1, image2;
cvtColor(image01, image1, CV_RGB2GRAY);
cvtColor(image02, image2, CV_RGB2GRAY);
//提取特征点
SurfFeatureDetector Detector(10000);
vector keyPoint1, keyPoint2;
Detector.detect(image1, keyPoint1);
Detector.detect(image2, keyPoint2);
/*特征点描述,为下边的特征点匹配做准备 */
cv::SurfDescriptorExtractor Descriptor;
Mat imageDesc1, imageDesc2;
Descriptor.compute(image1, keyPoint1, imageDesc1);
Descriptor.compute(image2, keyPoint2, imageDesc2);
cv::FlannBasedMatcher matcher;
cv::vector > matchePoints;
cv::vector GoodMatchePoints;
cv::vector train_desc(1, imageDesc1);
matcher.add(train_desc);
matcher.train();
matcher.knnMatch(imageDesc2, matchePoints, 2);
Mat first_match;
drawMatches(image02, keyPoint2, image01, keyPoint1, matchePoints, first_match);
// Lowe's algorithm,获取优秀匹配点
for (int i = 0; i < matchePoints.size(); i++)
{
if (matchePoints[i][0].distance < 0.8* matchePoints[i][1].distance)
{
GoodMatchePoints.push_back(matchePoints[i][0]);
}
}
Mat good_match;
drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, good_match);
// imshow("goodmatch",good_match);
cv::vector imagePoints1, imagePoints2;
for (int i = 0; ilabel_pano->clear();
imgpano= imgpano.scaled(ui->label_pano->width(),ui->label_pano->height());
ui->label_pano->setPixmap(QPixmap::fromImage(imgpano));
}
void MainWindow::CalcCorners(const Mat& H, const Mat& src)
{
double v2[] = { 0, 0, 1 };//左上角
double v1[3];//变换后的坐标值
Mat V2 = Mat(3, 1, CV_64FC1, v2); //列向量
Mat V1 = Mat(3, 1, CV_64FC1, v1); //列向量
V1 = H * V2;
//左上角(0,0,1)
/* cout << "V2: " << V2 << endl;
cout << "V1: " << V1 << endl;*/
corners.left_top.x = v1[0] / v1[2];
corners.left_top.y = v1[1] / v1[2];
//左下角(0,src.rows,1)
v2[0] = 0;
v2[1] = src.rows;
v2[2] = 1;
V2 = Mat(3, 1, CV_64FC1, v2); //列向量
V1 = Mat(3, 1, CV_64FC1, v1); //列向量
V1 = H * V2;
corners.left_bottom.x = v1[0] / v1[2];
corners.left_bottom.y = v1[1] / v1[2];
//右上角(src.cols,0,1)
v2[0] = src.cols;
v2[1] = 0;
v2[2] = 1;
V2 = Mat(3, 1, CV_64FC1, v2); //列向量
V1 = Mat(3, 1, CV_64FC1, v1); //列向量
V1 = H * V2;
corners.right_top.x = v1[0] / v1[2];
corners.right_top.y = v1[1] / v1[2];
//右下角(src.cols,src.rows,1)
v2[0] = src.cols;
v2[1] = src.rows;
v2[2] = 1;
V2 = Mat(3, 1, CV_64FC1, v2); //列向量
V1 = Mat(3, 1, CV_64FC1, v1); //列向量
V1 = H * V2;
corners.right_bottom.x = v1[0] / v1[2];
corners.right_bottom.y = v1[1] / v1[2];
}
void MainWindow::OptimizeSeam(Mat& img1, Mat& trans, Mat& dst)
{
int start = MIN(corners.left_top.x, corners.left_bottom.x);//开始位置,即重叠区域的左边界
double processWidth = img1.cols - start;//重叠区域的宽度
int rows = dst.rows;
int cols = img1.cols; //注意,是列数*通道数
double alpha = 1;//img1中像素的权重,初始化权重值
for (int i = 0; i < rows; i++)
{
uchar* p = img1.ptr(i); //获取第i行的首地址
uchar* t = trans.ptr(i);
uchar* d = dst.ptr(i);
for (int j = start; j < cols; j++)
{
//如果遇到图像trans中无像素的黑点,则完全拷贝img1中的数据
if (t[j * 3] == 0 && t[j * 3 + 1] == 0 && t[j * 3 + 2] == 0)
{
alpha = 1;
}
else
{
//img1中像素的权重,与当前处理点距重叠区域左边界的距离成正比,实验证明,这种方法确实好
alpha = (processWidth - (j - start)) / processWidth;
if (alpha>=0.5)
{
alpha = 1;
}
else
{
alpha = 0;
}
}
d[j * 3] = p[j * 3] * alpha + t[j * 3] * (1 - alpha);
d[j * 3 + 1] = p[j * 3 + 1] * alpha + t[j * 3 + 1] * (1 - alpha);
d[j * 3 + 2] = p[j * 3 + 2] * alpha + t[j * 3 + 2] * (1 - alpha);
}
}
}
结果如下: