拉普拉斯金字塔融合:
融合原理: step1:传入一个 mask掩膜; step2:分别计算 image1和 image2 的高斯金字塔和拉普拉斯金字塔。高斯金字塔是将原始图像按照一定的方式缩小若干次所得到的一系列图像。我们将缩小的每一幅图像比作金字塔的每一层。拉普拉斯金字塔原高斯金字塔的第 1 个level 的图像和这个经过 第 2 个level 图像上采样得到的图像相减(第 2 个 level 图像上采样之后就和原高斯金字塔第 1 个 level 图像的大小一样了),相减就会得到一个残差,这就是拉普拉斯金字塔的最高级; step3:计算得到 mask 的高斯金字塔;
step4:将 image1和 image2 高斯金字塔的顶层按照 mask 的权重(mask高斯金字塔的顶层)相加,得到复原的起点,记为 START; step5:将 image1 和 image2 的拉普拉斯金字塔每一层按照 mask 权重相加(mask高斯金字塔对应的层),得到混合的拉普拉斯金字塔 Blend_lp; step6:根据这个新的金字塔重建出最终的图像,具体来讲,先对 START 进行上采样,然后跟混合拉普拉斯金字塔 Blend_lp的顶层相加,得到B1,然后B1继续上采样,在和 Blend_lp的下一层相加,得到B2重复上述过程,最终得到融合的图像。
实现效果:
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
INCLUDEPATH += C:\opencv-3.4.5-64\include\
C:\opencv-3.4.5-64\include\opencv2\
C:\opencv-3.4.5-64\include\opencv
LIBS += -L C:\opencv-3.4.5-64\x64\mingw\lib\libopencv_*.a
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include
#include
#include
#include
#include
#include "opencv2/opencv.hpp"
#include
#include
using namespace cv;
using namespace std;
#include
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
QStringList fileList;
private slots:
void on_BtnPicture_clicked();
void on_BtnFusion_clicked();
void on_BtnClose_clicked();
private:
Ui::MainWindow *ui;
cv::Mat rightimg;
cv::Mat leftimg;
};
class LaplacianBlending {
private:
Mat_ left;
Mat_ right;
Mat_ blendMask;
vector > leftLapPyr, rightLapPyr, resultLapPyr;//Laplacian Pyramids
Mat leftHighestLevel, rightHighestLevel, resultHighestLevel;
vector > maskGaussianPyramid; //masks are 3-channels for easier multiplication with RGB
int levels;
void buildPyramids() {
buildLaplacianPyramid(left, leftLapPyr, leftHighestLevel);
buildLaplacianPyramid(right, rightLapPyr, rightHighestLevel);
buildGaussianPyramid();
}
void buildGaussianPyramid() {//金字塔内容为每一层的掩模
assert(leftLapPyr.size() > 0);
maskGaussianPyramid.clear();
Mat currentImg;
cvtColor(blendMask, currentImg, COLOR_GRAY2BGR);//store color img of blend mask into maskGaussianPyramid
maskGaussianPyramid.push_back(currentImg); //0-level
currentImg = blendMask;
for (int l = 1; l < levels + 1; l++) {
Mat _down;
if (leftLapPyr.size() > l)
pyrDown(currentImg, _down, leftLapPyr[l].size());
else
pyrDown(currentImg, _down, leftHighestLevel.size()); //lowest level
Mat down;
cvtColor(_down, down, COLOR_GRAY2BGR);
maskGaussianPyramid.push_back(down);//add color blend mask into mask Pyramid
currentImg = _down;
}
}
void buildLaplacianPyramid(const Mat& img, vector >& lapPyr, Mat& HighestLevel) {
lapPyr.clear();
Mat currentImg = img;
for (int l = 0; l < levels; l++) {
Mat down, up;
pyrDown(currentImg, down);
pyrUp(down, up, currentImg.size());
Mat lap = currentImg - up;
lapPyr.push_back(lap);
currentImg = down;
}
currentImg.copyTo(HighestLevel);
}
Mat_ reconstructImgFromLapPyramid() {
//将左右laplacian图像拼成的resultLapPyr金字塔中每一层
//从上到下插值放大并相加,即得blend图像结果
Mat currentImg = resultHighestLevel;
for (int l = levels - 1; l >= 0; l--) {
Mat up;
pyrUp(currentImg, up, resultLapPyr[l].size());
currentImg = up + resultLapPyr[l];
}
return currentImg;
}
void blendLapPyrs() {
//获得每层金字塔中直接用左右两图Laplacian变换拼成的图像resultLapPyr
resultHighestLevel = leftHighestLevel.mul(maskGaussianPyramid.back()) +
rightHighestLevel.mul(Scalar(1.0, 1.0, 1.0) - maskGaussianPyramid.back());
for (int l = 0; l < levels; l++) {
Mat A = leftLapPyr[l].mul(maskGaussianPyramid[l]);
Mat antiMask = Scalar(1.0, 1.0, 1.0) - maskGaussianPyramid[l];
Mat B = rightLapPyr[l].mul(antiMask);
Mat_ blendedLevel = A + B;
resultLapPyr.push_back(blendedLevel);
}
}
public:
LaplacianBlending(const Mat_& _left, const Mat_& _right, const Mat_& _blendMask, int _levels) ://construct function, used in LaplacianBlending lb(l,r,m,4);
left(_left), right(_right), blendMask(_blendMask), levels(_levels)
{
assert(_left.size() == _right.size());
assert(_left.size() == _blendMask.size());
buildPyramids(); //construct Laplacian Pyramid and Gaussian Pyramid
blendLapPyrs(); //blend left & right Pyramids into one Pyramid
};
Mat_ blend() {
return reconstructImgFromLapPyramid();//reconstruct Image from Laplacian Pyramid
}
};
#endif // MAINWINDOW_H
mainwindow.cpp:
#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::on_BtnPicture_clicked()
{
QWidget *widget = new QWidget;
QVBoxLayout *layout = new QVBoxLayout; //设置垂直布局
widget->setLayout(layout);
QString curPath = QDir::currentPath();
QString dlgTitle = "选择多个文件";
QString filter = "图片文件(*.jpg *.gif *.tif *.png) ";
QStringList fileList = QFileDialog::getOpenFileNames(this,dlgTitle,curPath,filter);
qDebug()<setPixmap(QPixmap::fromImage(qImage.scaled(300, 300, Qt::KeepAspectRatio)));
QScrollArea *scrollArea = new QScrollArea(this);
scrollArea->setBackgroundRole(QPalette::Dark); //设置背景色
scrollArea->setWidgetResizable(true); // 自动调整大小
scrollArea->setAlignment(Qt::AlignCenter); // 居中对齐
label->setAlignment(Qt::AlignCenter); // 图片居中
layout->addWidget(label);
ui->scrollArea->setWidget(widget);
}
}
Mat_ LaplacianBlend(const Mat_& l, const Mat_& r, const Mat_& m) {
LaplacianBlending lb(l, r, m, 4);
return lb.blend();
}
void MainWindow::on_BtnFusion_clicked()
{
vector images;
images.push_back(leftimg);
images.push_back(rightimg);
int hight = leftimg.rows;
int width = leftimg.cols;
Mat leftImg32f, rightImg32f;
leftimg.convertTo(leftImg32f, CV_32F);
rightimg.convertTo(rightImg32f, CV_32F);
//创建用于混合的掩膜,这里在中间进行混合
Mat mask = Mat::zeros(hight, width, CV_32FC1);
mask(Range::all(), Range(0, mask.cols * 0.5)) = 1.0;
Mat blendImg = LaplacianBlend(leftImg32f, rightImg32f, mask);
blendImg.convertTo(blendImg, CV_8UC3);
//读取图片
// readImages(imageLabel);
//对齐图片
Ptr alignMTB = createAlignMTB();
alignMTB->process(images, images);
//曝光合成(Mertens是该论文的作者)被OpenCV集成为函数了
Mat Fusion;
Ptr mergeMertens = createMergeMertens();
mergeMertens->process(images,Fusion);
//imshow("fusion", Fusion);
Fusion = Fusion * 255;
imwrite("fusion.png",Fusion);
cv::Mat dst = imread("fusion.png");
QImage mImage(dst.data, dst.cols,
dst.rows, dst.step, QImage::Format_RGB888);
mImage = mImage.rgbSwapped();
mImage = mImage.scaled(ui->labPicfusion->size(),Qt::KeepAspectRatio);
ui->labPicfusion->setPixmap(QPixmap::fromImage(mImage));
ui->labPicfusion->setScaledContents(true);
}
void MainWindow::on_BtnClose_clicked()
{
this->close();
}
main.cpp:没有变动
mainwindow.ui: