图像融合之拉普拉斯融合(laplacian blending)
一、拉普拉斯融合基本步骤
1. 两幅图像L,R,以及二值掩模mask,给定金字塔层数level。
2. 分别根据L,R构建其对应的拉普拉斯残差金字塔(层数为level),并保留高斯金字塔下采样最顶端的图像(尺寸最小的图像,第level+1层):
拉普拉斯残差金字塔构建方法如下,以L图为例:
(1) 对L进行高斯下采样得到downL,OpenCV中pyrDown()函数可以实现此功能。然后再对downL进行高斯上采样得到upL,OpenCV中pyrUp()函数可以实现此功能。
(2) 计算原图L与upL之间的残差,得到一幅残差图lapL0。作为残差金字塔最低端的图像。
(3) 对downL继续进行(1) (2)操作,不断计算残差图lapL1, lap2, lap3…..lapN。这样得到一系列残差图,即为拉普拉斯残差金字塔。
(4)拉普拉斯 残差金字塔中一共有level幅图像。而我们需要保留第level+1层的高斯下采样图topL,以便后面使用。
3. 二值掩模mask下采样构建高斯金字塔,同样利用pyrDown()实现,共有level+1层。
4. 利用mask金字塔每一层的mask图,将L图和R图的拉普拉斯残差金字塔对应层的图像合并为一幅图像。这样得到合并后的拉普拉斯残差金字塔。同时利用最顶端的mask将步骤2中保留的topL和topR合并为topLR。
5. 以topLR为金字塔最顶端的图像,利用pyrUp()函数对topLR进行高斯上采样,得到upTopLR,并将upTopLR与步骤4中合并后的残差金字塔对应层的图像相加,重建出该层的图像。
6. 重复步骤5,直至重建出第0层,也就是金字塔最低端的图像,即blendImg。输出。
拉普拉斯金字塔的OpenCV实现代码如下:
1 #include <opencv2/opencv.hpp> 2 #include <iostream> 3 #include <string> 4 5 using namespace std; 6 using namespace cv; 7 8 /************************************************************************/ 9 /* 说明: 10 *金字塔从下到上依次为 [0,1,...,level-1] 层 11 *blendMask 为图像的掩模 12 *maskGaussianPyramid为金字塔每一层的掩模 13 *resultLapPyr 存放每层金字塔中直接用左右两图Laplacian变换拼成的图像 14 */ 15 /************************************************************************/ 16 17 18 class LaplacianBlending { 19 private: 20 Mat left; 21 Mat right; 22 Mat blendMask; 23 24 //Laplacian Pyramids 25 vector<Mat> leftLapPyr, rightLapPyr, resultLapPyr; 26 Mat leftHighestLevel, rightHighestLevel, resultHighestLevel; 27 //mask为三通道方便矩阵相乘 28 vector<Mat> maskGaussianPyramid; 29 30 int levels; 31 32 void buildPyramids() 33 { 34 buildLaplacianPyramid(left, leftLapPyr, leftHighestLevel); 35 buildLaplacianPyramid(right, rightLapPyr, rightHighestLevel); 36 buildGaussianPyramid(); 37 } 38 39 void buildGaussianPyramid() 40 { 41 //金字塔内容为每一层的掩模 42 assert(leftLapPyr.size()>0); 43 44 maskGaussianPyramid.clear(); 45 Mat currentImg; 46 cvtColor(blendMask, currentImg, CV_GRAY2BGR); 47 //保存mask金字塔的每一层图像 48 maskGaussianPyramid.push_back(currentImg); //0-level 49 50 currentImg = blendMask; 51 for (int l = 1; l<levels + 1; l++) { 52 Mat _down; 53 if (leftLapPyr.size() > l) 54 pyrDown(currentImg, _down, leftLapPyr[l].size()); 55 else 56 pyrDown(currentImg, _down, leftHighestLevel.size()); //lowest level 57 58 Mat down; 59 cvtColor(_down, down, CV_GRAY2BGR); 60 //add color blend mask into mask Pyramid 61 maskGaussianPyramid.push_back(down); 62 currentImg = _down; 63 } 64 } 65 66 void buildLaplacianPyramid(const Mat& img, vector<Mat>& lapPyr, Mat& HighestLevel) 67 { 68 lapPyr.clear(); 69 Mat currentImg = img; 70 for (int l = 0; l<levels; l++) { 71 Mat down, up; 72 pyrDown(currentImg, down); 73 pyrUp(down, up, currentImg.size()); 74 Mat lap = currentImg - up; 75 lapPyr.push_back(lap); 76 currentImg = down; 77 } 78 currentImg.copyTo(HighestLevel); 79 } 80 81 Mat reconstructImgFromLapPyramid() 82 { 83 //将左右laplacian图像拼成的resultLapPyr金字塔中每一层 84 //从上到下插值放大并与残差相加,即得blend图像结果 85 Mat currentImg = resultHighestLevel; 86 for (int l = levels - 1; l >= 0; l--) 87 { 88 Mat up; 89 pyrUp(currentImg, up, resultLapPyr[l].size()); 90 currentImg = up + resultLapPyr[l]; 91 } 92 return currentImg; 93 } 94 95 void blendLapPyrs() 96 { 97 //获得每层金字塔中直接用左右两图Laplacian变换拼成的图像resultLapPyr 98 resultHighestLevel = leftHighestLevel.mul(maskGaussianPyramid.back()) + 99 rightHighestLevel.mul(Scalar(1.0, 1.0, 1.0) - maskGaussianPyramid.back()); 100 for (int l = 0; l<levels; l++) 101 { 102 Mat A = leftLapPyr[l].mul(maskGaussianPyramid[l]); 103 Mat antiMask = Scalar(1.0, 1.0, 1.0) - maskGaussianPyramid[l]; 104 Mat B = rightLapPyr[l].mul(antiMask); 105 Mat blendedLevel = A + B; 106 107 resultLapPyr.push_back(blendedLevel); 108 } 109 } 110 111 public: 112 LaplacianBlending(const Mat& _left, const Mat& _right, const Mat& _blendMask, int _levels) ://construct function, used in LaplacianBlending lb(l,r,m,4); 113 left(_left), right(_right), blendMask(_blendMask), levels(_levels) 114 { 115 assert(_left.size() == _right.size()); 116 assert(_left.size() == _blendMask.size()); 117 //创建拉普拉斯金字塔和高斯金字塔 118 buildPyramids(); 119 //每层金字塔图像合并为一个 120 blendLapPyrs(); 121 }; 122 123 Mat blend() 124 { 125 //重建拉普拉斯金字塔 126 return reconstructImgFromLapPyramid(); 127 } 128 }; 129 130 Mat LaplacianBlend(const Mat &left, const Mat &right, const Mat &mask) 131 { 132 LaplacianBlending laplaceBlend(left, right, mask, 10); 133 return laplaceBlend.blend(); 134 } 135 136 int main() { 137 Mat img8UL = imread("data/apple.jpg"); 138 Mat img8UR = imread("data/orange.jpg"); 139 140 int imgH = img8UL.rows; 141 int imgW = img8UL.cols; 142 143 imshow("left", img8UL); 144 imshow("right", img8UR); 145 146 Mat img32fL, img32fR; 147 img8UL.convertTo(img32fL, CV_32F); 148 img8UR.convertTo(img32fR, CV_32F); 149 150 //创建mask 151 Mat mask = Mat::zeros(imgH, imgW, CV_32FC1); 152 mask(Range::all(), Range(0, mask.cols * 0.5)) = 1.0; 153 154 Mat blendImg = LaplacianBlend(img32fL, img32fR, mask); 155 156 blendImg.convertTo(blendImg, CV_8UC3); 157 imshow("blended", blendImg); 158 159 waitKey(0); 160 return 0; 161 }
View Code
融合结果如下图:
金字塔层数level=5
金字塔层数level=10