图像融合这一块之前几乎可以说零接触,最近再看深度学习的相关内容,看到卷积神经网络(CNN)那里池化的概念的时候,作者说如果之前对图像金字塔分割与融合有一个很好的了解的时候会好理解一些,(其实也并不是很难理解,之前sift hog 的内容都有涉及到金字塔,但是出于自己对知识的渴望,再加之我确实想了解了解融合相关的知识)于是不管说的是不是真的,我决定了解了解它。
主要参考博客:(几乎都是他们的内容)
CNN讲解:卷积神经网络(CNN)入门
拉普拉斯金字塔融合:
浅墨大神:http://blog.csdn.net/poem_qianmo/article/details/26157633
浙大女神:http://blog.csdn.net/abcjennifer/article/details/7628655 (不用怀疑了,网上这个拉普拉斯融合代码几乎都是她的模板,我也是抄他的)
博客园融合大满贯:http://www.cnblogs.com/silence-hust/p/4193208.html (讲的很全面)
CSDN博主:http://blog.csdn.net/u011100984/article/details/37876355(很可能也是位美女)
好的,继续我们的二次元,然后正文(哈哈)
我们的正文:
高斯金字塔:高斯金字塔是最基本的图像塔。首先将原图像作为最底层图像G0(高斯金字塔的第0层),利用高斯核(5*5)对其进行卷积,然后对卷积后的图像进行下采样(去除偶数行和列)得到上一层图像G1,将此图像作为输入,重复卷积和下采样操作得到更上一层图像,反复迭代多次,形成一个金字塔形的图像数据结构,即高斯金字塔。
为了获取层级为 G_i+1 的金字塔图像,我们采用如下方法:
<1>对图像G_i进行高斯内核卷积
<2>将所有偶数行和列去除
得到的图像即为G_i+1的图像,显而易见,结果图像只有原图的四分之一。通过对输入图像G_i(原始图像)不停迭代以上步骤就会得到整个金字塔。同时我们也可以看到,向下取样会逐渐丢失图像的信息。
以上就是对图像的向下取样操作,即缩小图像。
Opencv中使用pyrdown函数就可以获得高斯金字塔。
拉普拉斯金字塔:在高斯金字塔的运算过程中,图像经过卷积和下采样操作会丢失部分高频细节信息。为描述这些高频信息,人们定义了拉普拉斯金字塔(Laplacian Pyramid, LP)。用高斯金字塔的每一层图像减去其上一层图像上采样并高斯卷积之后的预测图像,得到一系列的差值图像即为 LP 分解图像。
如果想放大图像,则需要通过向上取样操作得到,具体做法如下:
<1>将图像在每个方向扩大为原来的两倍,新增的行和列以0填充
<2>使用先前同样的内核(乘以4)与放大后的图像卷积,获得 “新增像素”的近似值
得到的图像即为放大后的图像,但是与原来的图像相比会发觉比较模糊,因为在缩放的过程中已经丢失了一些信息,如果想在缩小和放大整个过程中减少信息的丢失,这些数据形成了拉普拉斯金字塔。
上述公式看明白了,接下来这张整个拉普拉斯金字塔运算过程图就很好的理解:(看到文章很多公式就很烦,我很理解,我也是这样的,但是上述公式很重要)
高斯金字塔和拉普拉斯金字塔的运算过程明白了,我们再来看它的融合应用
PROPERTY:图像拉普拉斯金字塔分解的目的是将源图像分别分解到不同的空间频带上,融合过程是在各空间频率层上分别进行的,这样就可以针对不同分解层的不同频带上的特征与细节,采用不同的融合算子以达到突出特定频带上特征与细节的目的。即有可能将来自不同图像的特征与细节融合在一起。
具体实现步骤:
用金字塔进行图像融合的主要步骤如下:
1.有两张原图像,还有一张模板图(用来确定用左边图像的哪部分,右边图像的哪部分来组合成最终的结果);
Mat_ left;
Mat_ right;
Mat_ blendMask;
2.有了原图之后就要进行分割求金字塔图像
vector > leftLapPyr,rightLapPyr,maskGaussianPyramid;
不过leftLapPyr和rightLapPyr保存的是拉普拉斯金字塔,maskGaussianPyramid保存的是高斯金字塔,我们知道图下采样之后是DOG,图减去DOG上采样之后是LOG,所以如果对最顶层的图不断上采样并加上LOG,最终会得到原图下采样的结果。
对左右原图进行拉普拉斯金字塔就是为了能够获得每层的细节信息,并且使用mask高斯金字塔每层对应的模板 进行融合,这就相当于得到了一系列的LOG,那对这些LOG不断的上采样就得到了最后的逼近真实的融合图。
另外一个原因就是在采样的过程中有模糊处理,所以融合的边缘不会是突兀的,而是很自然的呈现。
3.分别得到了两图的LOG之后,就要通过maskGaussianPyramid得到融合之后的LOG了。
vector >resultLapPyr
如果看的过程中稍有些代码不熟悉,自行百度不成问题,代码本身不难看懂,但是自己写却是另外一回事,让人佩服
#include
#include
#include
using namespace cv;
/*************************************************/
/*说明:
*金字塔从下到上依次为 [0,1,....,level-1]层
*blendMask 为图像的掩模
*maskGaussianPyramid为金字塔每一层的掩模
*resultLapPyr 存放每层金字塔中直接用左右图Laplacian变换拼成的图像
*/
/****************************************************************/
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, CV_GRAY2BGR);//store color img of blend mask
maskGaussianPyramid.push_back(currentImg); //0-level
currentImg = blendMask;
for (int l=1; l l)
pyrDown(currentImg, _down, leftLapPyr[l].size());
else
pyrDown(currentImg, _down, leftHighestLevel.size()); //lowest level
Mat down;
cvtColor(_down, down, CV_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 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 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
}
};
Mat_ LaplacianBlend(const Mat_& l, const Mat_& r, const Mat_& m) {
LaplacianBlending lb(l,r,m,4);
return lb.blend();
}
int main(){
Mat l8u = imread("greenapple.jpg");
Mat r8u = imread("oriange.jpg");
imshow("left",l8u);
imshow("right",r8u);
Mat_ l; l8u.convertTo(l,CV_32F,1.0/255.0);//Vec3f表示有三个通道,即 1[row][column][depth]
Mat_ r; r8u.convertTo(r,CV_32F,1.0/255.0);
/******************** void convertTo
( OutputArray m, int rtype, double alpha=1, double beta=0 ) const;***************/
/* Performs linear transformation on every soyrce array element:
dst(x,y,c) = scale*src(x,y,alpha)+beta.
Arbitrary combination of input and output array depths are allowed
(number of channels must be the same), thus the function can be used
for type conversion*/
//create blend mask matrix m
Mat_ m(l.rows,l.cols,0.0); //将m全部赋值为0
m(Range::all(),Range(0,m.cols/2)) = 1.0; //取m全部行&[0,m.cols/2]列,赋值为1.0
Mat_ blend = LaplacianBlend(l, r, m);
imshow("blended",blend);
waitKey(0);
return 0;
}
图片我是自己百度的,代码是别人的,就不上传赚取积分了,有任何问题欢迎留言交流!
还是那句话!你永远不知道你的下一个错误是什么?有任何问题欢迎交流,一起加油!