使用liner、feather、multiband对已经拼接的数据进行融合(下)
理解mulitband。所谓的mulitband,其实就是一种多尺度的样条融合,其实现的主要方法就是laplace金字塔。
;
高斯金字塔是向下采样,而laplace金字塔式向上采样(也就是恢复),采用的都是差值的方法。如何能够在金字塔各个层次上面进行图像的融合,结果证明是相当不错的。网络上面流传的一个类解释了这个问题,并且能够拿来用:
#
include
"stdafx.h"
#
include
<
iostream
>
#
include
<
vector
>
#
include
<
opencv2
/
core
/
core.hpp
>
#
include
<
opencv2
/
imgproc
/
imgproc.hpp
>
#
include
<
opencv2
/
highgui
/
highgui.hpp
>
#
include
<
opencv2
/
features2d
/
features2d.hpp
>
#
include
<
opencv2
/
calib3d
/
calib3d.hpp
>
using
namespace
cv;
#
ifdef
_DEBUG
#
define
new
DEBUG_NEW
#
endif
#
define
DllExport _declspec (dllexport)
/*
1.设计一个mask(一半全1,一半全0),并计算level层的gaussion_mask[i];
2.计算两幅图像每一层的Laplacian[i],并与gaussion_mask[i]相乘,合成一幅result_lapacian[i];
3.对两幅图像不断求prydown,并把最高层保存在gaussion[i],与gaussion_mask[i]相乘,合成一幅result_gaussion;
4,对result_gaussion不断求pryup,每一层都与result_lapacian[i]合成,最后得到原-图像大小的融合图像。
*/
class
LaplacianBlending {
private
:
Mat_
<
Vec3f
>
top;
Mat_
<
Vec3f
>
down;
Mat_
<
float
>
blendMask;
vector
<
Mat_
<
Vec3f
>
>
topLapPyr,downLapPyr,resultLapPyr;
//Laplacian Pyramids
Mat topHighestLevel, downHighestLevel, resultHighestLevel;
vector
<
Mat_
<
Vec3f
>
>
maskGaussianPyramid;
//masks are 3-channels for easier multiplication with RGB
int
levels;
//创建金字塔
void
buildPyramids() {
buildLaplacianPyramid(top,topLapPyr,topHighestLevel);
buildLaplacianPyramid(down,downLapPyr,downHighestLevel);
buildGaussianPyramid();
}
//创建gauss金字塔
void
buildGaussianPyramid() {
//金字塔内容Y为a每一层的掩模
assert(topLapPyr.size()
>
0
);
maskGaussianPyramid.clear();
Mat currentImg;
//blendMask就是掩码
cvtColor(blendMask, currentImg, CV_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
(topLapPyr.size()
>
l)
pyrDown(currentImg, _down, topLapPyr[l].size());
else
pyrDown(currentImg, _down, topHighestLevel.size());
//lowest level
Mat down;
cvtColor(_down, down, CV_GRAY2BGR);
maskGaussianPyramid.push_back(down);
//add color blend mask into mask Pyramid
currentImg
=
_down;
}
}
//创建laplacian金字塔
void
buildLaplacianPyramid(
const
Mat
&
img, vector
<
Mat_
<
Vec3f
>
>
&
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;
//存储的就是残D差
lapPyr.push_back(lap);
currentImg
=
down;
}
currentImg.copyTo(HighestLevel);
}
Mat_
<
Vec3f
>
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变换拼成的图像
//一半的一半就是在这个地方计算的。 是基于掩模的方式进行的.
resultHighestLevel
=
topHighestLevel.mul(maskGaussianPyramid.back())
+
downHighestLevel.mul(Scalar(
1
.
0
,
1
.
0
,
1
.
0
)
-
maskGaussianPyramid.back());
for
(
int
l
=
0
; l
<
levels; l
++
) {
Mat A
=
topLapPyr[l].mul(maskGaussianPyramid[l]);
Mat antiMask
=
Scalar(
1
.
0
,
1
.
0
,
1
.
0
)
-
maskGaussianPyramid[l];
Mat B
=
downLapPyr[l].mul(antiMask);
Mat_
<
Vec3f
>
blendedLevel
=
A
+
B;
resultLapPyr.push_back(blendedLevel);
}
}
public
:
LaplacianBlending(
const
Mat_
<
Vec3f
>
&
_top,
const
Mat_
<
Vec3f
>
&
_down,
const
Mat_
<
float
>
&
_blendMask,
int
_levels)
:
//缺省数y据Y,使1用 LaplacianBlending lb(l,r,m,4);
top(_top),down(_down),blendMask(_blendMask),levels(_levels)
{
assert(_top.size()
==
_down.size());
assert(_top.size()
==
_blendMask.size());
buildPyramids();
//创建laplacian金字塔和gauss金字塔
blendLapPyrs();
//将左右金字塔融合成为a一个图片
};
Mat_
<
Vec3f
>
blend() {
return
reconstructImgFromLapPyramid();
//reconstruct Image from Laplacian Pyramid
}
};
Mat_
<
Vec3f
>
LaplacianBlend(
const
Mat_
<
Vec3f
>
&
t,
const
Mat_
<
Vec3f
>
&
d,
const
Mat_
<
float
>
&
m) {
LaplacianBlending lb(t,d,m,
4
);
return
lb.blend();
}
DllExport
double
aValue
=
1
.
5
;
DllExport
int
dlladd()
{
return
5
;
}
DllExport
int
dlladd(
int
a,
int
b)
{
return
a
+
b;
}
DllExport cv
:
:
Mat imagetest()
{
cv
:
:
Mat image1
=
cv
:
:
imread(
"C:\\apple.png"
,
1
);
cv
:
:
Mat image2
=
cv
:
:
imread(
"C:\\orange.png"
,
1
);
Mat_
<
Vec3f
>
t; image1.convertTo(t,CV_32F,
1
.
0
/
255
.
0
);
//Vec3f表示有三个通道,即 [row][column][depth]
Mat_
<
Vec3f
>
d; image2.convertTo(d,CV_32F,
1
.
0
/
255
.
0
);
Mat_
<
float
>
m(t.rows,d.cols,
0
.
0
);
//将m全部赋3值为a0
//m(Range::all(),Range(0,m.cols/2)) = 1.0; //原来初始的掩码是在这里
m(Range(
0
,m.rows
/
2
),Range
:
:
all())
=
1
.
0
;
Mat_
<
Vec3f
>
blend
=
LaplacianBlend(t,d, m);
imshow(
"blended"
,blend);
return
blend;
}
需要注意的是, m(Range(0,m.rows/2),Range::all())=1.0表明了原始图像的掩码,这个掩码就是那个分界的地方。
比如比如:
永达实际的项目上面,应该是这样。
在使用这种方法进行大规模拼接的时候,主要是两个问题
一个是效果,在上面的那个橘子苹果的例子中,只有前景的颜色有变化,实际上其它几个地方色彩亮度变化不是很大。但是对于实际情况下的拼接来说,亮度有变化,比较难以处理。
二是效率。laplacian需要大量的过程,造成结果内存的需求很大,一时半会很难优化好。multband看来只能够在符合要求的简单拼接中去实现使用。