1. 程式人生 > >影象融合之拉普拉斯融合(laplacian blending)

影象融合之拉普拉斯融合(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

  

  附上自己實現pyrDown和pyrUp寫的拉普拉斯融合,僅供參考:

  1 #include <opencv2\opencv.hpp>
  2 #include <iostream>
  3 #include <string>
  4 
  5 using namespace std;
  6 
  7 //#define DEBUG
  8 
  9 
 10 void borderInterp(cv::Mat &_src, int radius)
 11 {
 12     int imgH = _src.rows;
 13     int imgW = _src.cols;
 14     float *pSrc = (float*)_src.data;
 15     for (int i = radius; i < imgH-radius*2; i++)
 16     {
 17         for (int j = 0; j < 2; j++)
 18         {
 19             int srcIdx = (i*imgW + j + 3) * 3;
 20             int dstIdx = (i*imgW + j) * 3;
 21             pSrc[dstIdx] = pSrc[srcIdx];
 22             pSrc[dstIdx + 1] = pSrc[srcIdx + 1];
 23             pSrc[dstIdx + 2] = pSrc[srcIdx + 2];
 24         }
 25         for (int j = imgW - radius; j < imgW; j++)
 26         {
 27             int srcIdx = (i*imgW + j - 3) * 3;
 28             int dstIdx = (i*imgW + j) * 3;
 29             pSrc[dstIdx] = pSrc[srcIdx];
 30             pSrc[dstIdx + 1] = pSrc[srcIdx + 1];
 31             pSrc[dstIdx + 2] = pSrc[srcIdx + 2];
 32 
 33         }
 34     }
 35     for (int j = 0; j < imgW; j++)
 36     {
 37         for (int i = 0; i < 2; i++)
 38         {
 39             int srcIdx = ((i + 3)*imgW + j) * 3;
 40             int dstIdx = (i*imgW + j) * 3;
 41             pSrc[dstIdx] = pSrc[srcIdx];
 42             pSrc[dstIdx + 1] = pSrc[srcIdx + 1];
 43             pSrc[dstIdx + 2] = pSrc[srcIdx + 2];
 44         }
 45         for (int i = imgH - radius; i < imgH; i++)
 46         {
 47             int srcIdx = ((i - 3)*imgW + j) * 3;
 48             int dstIdx = (i*imgW + j) * 3;
 49             pSrc[dstIdx] = pSrc[srcIdx];
 50             pSrc[dstIdx + 1] = pSrc[srcIdx + 1];
 51             pSrc[dstIdx + 2] = pSrc[srcIdx + 2];
 52         }
 53     }
 54 }
 55 
 56 void myPyrDown(cv::Mat src, cv::Mat &dst, cv::Size dSize)
 57 {
 58     dSize = dSize.area() == 0 ? cv::Size((src.cols + 1) / 2, (src.rows + 1) / 2) : dSize;
 59 
 60     float scale = 1. / 16;
 61 
 62     int imgH = src.rows;
 63     int imgW = src.cols;
 64     cv::Mat _src = cv::Mat::zeros(imgH + 4, imgW + 4, CV_32FC3);
 65     int _imgH = _src.rows;
 66     int _imgW = _src.cols;
 67     src.copyTo(_src(cv::Rect(2, 2, imgW, imgH)));
 68     borderInterp(_src, 2);
 69 
 70     //高斯卷積
 71     cv::Mat gaussImg = cv::Mat::zeros(imgH, imgW, CV_32FC3);
 72     cv::Mat tmpRowGaussImg = _src.clone();
 73     float *pSrc = (float*)_src.data;
 74     float *pRowGaussImg = (float*)tmpRowGaussImg.data;
 75     //行卷積
 76     for (int i = 2; i < imgH+2; i++)
 77     {
 78         for (int j = 2; j < imgW+2; j++)
 79         {
 80             float val[3] = { 0 };
 81             int idx = i*_imgW + j;
 82             for (int chan = 0; chan < 3; chan++)
 83             {
 84                 val[chan] += pSrc[(idx - 2) * 3 + chan] + pSrc[(idx + 2) * 3 + chan]
 85                     + 4 * (pSrc[(idx - 1) * 3 + chan] + pSrc[(idx + 1) * 3 + chan])
 86                     + 6 * pSrc[idx * 3 + chan];
 87             }
 88             pRowGaussImg[idx * 3] = val[0] * scale;
 89             pRowGaussImg[idx * 3 + 1] = val[1] * scale;
 90             pRowGaussImg[idx * 3 + 2] = val[2] * scale;
 91         }
 92     }
 93 
 94     float *pGaussImg = (float*)gaussImg.data;
 95     //列卷積
 96     for (int j = 0; j < imgW; j++)
 97     {
 98         for (int i = 0; i < imgH; i++)
 99         {
100             int gi = i + 2;
101             int gj = j + 2;
102             float val[3] = { 0 };
103             int idx = gi*_imgW + gj;
104             for (int chan = 0; chan < 3; chan++)
105             {
106                 val[chan] += pRowGaussImg[(idx-2*_imgW) * 3 + chan] + pRowGaussImg[(idx + 2*_imgW) * 3 + chan]
107                     + 4 * (pRowGaussImg[(idx - _imgW) * 3 + chan] + pRowGaussImg[(idx + _imgW) * 3 + chan])
108                     + 6 * pRowGaussImg[idx * 3 + chan];
109             }
110             
111             int id = (i*imgW + j) * 3;
112             pGaussImg[id] = val[0] * scale;
113             pGaussImg[id + 1] = val[1] * scale;
114             pGaussImg[id + 2] = val[2] * scale;
115         }
116     }
117 
118     int downH = dSize.height;
119     int downW = dSize.width;
120 
121     if (abs(downH * 2 - imgH) > 2) downH = imgH*0.5;
122     if (abs(downW * 2 - imgW) > 2) downW = imgW*0.5;
123     downH = (downH < 1) ? 1 : downH;
124     downW = (downW < 1) ? 1 : downW;
125 
126     dst = cv::Mat::zeros(downH, downW, CV_32FC3);
127     float *pDst = (float*)dst.data;
128     for (int i = 0; i < imgH; i++)
129     {
130         for (int j = 0; j < imgW; j++)
131         {
132             if (i % 2 != 0 || j % 2 != 0) continue;
133             int srcIdx = (i*imgW + j) * 3;
134             int y = int((i+1) * 0.5);
135             int x = int((j+1) * 0.5);
136             y = (y >= downH) ? (downH - 1) : y;
137             x = (x >= downW) ? (downW - 1) : x;
138             int dstIdx = (y*downW + x) * 3;
139             pDst[dstIdx] = pGaussImg[srcIdx];
140             pDst[dstIdx + 1] = pGaussImg[srcIdx + 1];
141             pDst[dstIdx + 2] = pGaussImg[srcIdx + 2];
142         }
143     }
144 }
145 
146 void myPyrUp(cv::Mat src, cv::Mat &dst, cv::Size dSize)
147 {
148     dSize = dSize.area() == 0 ? cv::Size(src.cols * 2, src.rows * 2) : dSize;
149     cv::Mat _src;
150     src.convertTo(_src, CV_32FC3);
151 
152     float scale = 1. / 8;
153 
154     int imgH = src.rows;
155     int imgW = src.cols;
156     int upImgH = dSize.height;
157     int upImgW = dSize.width;
158 
159     if (abs(upImgH - imgH * 2) > upImgH % 2) upImgH = imgH*2;
160     if (abs(upImgW - imgW * 2) > upImgW % 2) upImgW = imgW*2;
161 
162     cv::Mat upImg = cv::Mat::zeros(upImgH, upImgW, CV_32FC3);
163     float *pSrc = (float*)_src.data;
164     float *pUpImg = (float*)upImg.data;
165     for (int i = 0; i < upImgH; i++)
166     {
167         for (int j = 0; j < upImgW; j++)
168         {
169             if (i % 2 != 0 || j % 2 != 0) continue;
170             int dstIdx = (i*upImgW + j)*3;
171             int y = int((i+1)*0.5);
172             int x = int((j+1)*0.5);
173             y = (y >= imgH) ? (imgH - 1) : y;
174             x = (x >= imgW) ? (imgW - 1) : x;
175             int srcIdx = (y*imgW + x) * 3;
176 
177             pUpImg[dstIdx] = pSrc[srcIdx];
178             pUpImg[dstIdx + 1] = pSrc[srcIdx + 1];
179             pUpImg[dstIdx + 2] = pSrc[srcIdx + 2];
180         }
181     }
182 
183     dst = cv::Mat::zeros(dSize, CV_32FC3);
184     cv::Mat _upImg = cv::Mat::zeros(upImgH + 4, upImgW + 4, CV_32FC3);
185     int _imgH = _upImg.rows;
186     int _imgW = _upImg.cols;
187     upImg.copyTo(_upImg(cv::Rect(2, 2, upImgW, upImgH)));
188     borderInterp(_upImg, 2);
189 
190     //高斯卷積
191     cv::Mat tempRowGaussImg = _upImg.clone();
192     float *pUpData = (float*)_upImg.data;
193     float *pRowGaussImg = (float*)tempRowGaussImg.data;
194     //行卷積
195     for (int i = 2; i < upImgH + 2; i++)
196     {
197         for (int j = 2; j < upImgW + 2; j++)
198         {
199             float val[3] = { 0 };
200             int idx = i*_imgW + j;
201             for (int chan = 0; chan < 3; chan++)
202             {
203                 val[chan] += pUpData[(idx - 2) * 3 + chan] + pUpData[(idx + 2) * 3 + chan]
204                     + 4 * (pUpData[(idx - 1) * 3 + chan] + pUpData[(idx + 1) * 3 + chan])
205                     + 6 * pUpData[idx * 3 + chan];
206             }
207 
208             pRowGaussImg[idx * 3] = val[0] * scale;
209             pRowGaussImg[idx * 3 + 1] = val[1] * scale;
210             pRowGaussImg[idx * 3 + 2] = val[2] * scale;
211         }
212     }
213 
214 
215     //列卷積
216     float *pDst = (float*)dst.data;
217     for (int j = 0; j < upImgW; j++)
218     {
219         for (int i = 0; i < upImgH; i++)
220         {
221             int gi = i + 2;
222             int gj = j + 2;
223             float val[3] = { 0 };
224             int idx = gi*_imgW + gj;
225             for (int chan = 0; chan < 3; chan++)
226             {
227                 val[chan] += pRowGaussImg[(idx - 2 * _imgW) * 3 + chan] + pRowGaussImg[(idx + 2 * _imgW) * 3 + chan]
228                     + 4 * (pRowGaussImg[(idx - _imgW) * 3 + chan] + pRowGaussImg[(idx + _imgW) * 3 + chan])
229                     + 6 * pRowGaussImg[idx * 3 + chan];
230             }
231 
232             int id = (i*upImgW + j) * 3;
233             pDst[id] = val[0] * scale;
234             pDst[id + 1] = val[1] * scale;
235             pDst[id + 2] = val[2] * scale;
236         }
237     }
238 }
239 
240 void buildLaplacianPyramid(cv::Mat srcImg, vector<cv::Mat> &pyramidImgs, cv::Mat &topLevelImg, int levels)
241 {
242     cv::Mat currentImg = srcImg;
243     for (int k = 0; k < levels; k++)
244     {
245         cv::Mat downImg, upImg, lpImg;
246 
247 #ifdef DEBUG
248         cv::pyrDown(currentImg, downImg);
249         cv::pyrUp(downImg, upImg, currentImg.size());
250 #else
251         myPyrDown(currentImg, downImg, cv::Size());
252         myPyrUp(downImg, upImg, currentImg.size());
253 #endif
254 
255         lpImg = currentImg - upImg;
256         pyramidImgs.push_back(lpImg);
257         currentImg = downImg;
258     }
259     currentImg.copyTo(topLevelImg);
260 }
261 
262 void buildGaussPyramid(cv::Mat mask, vector<cv::Mat> &maskGaussPyramidImgs, vector<cv::Mat> pyramidImgs,cv::Mat topLevelImg, int levels)
263 {
264     cv::Mat currentMask;
265     //mask轉3通道
266     if (mask.channels() == 1)
267     {
268         cv::cvtColor(mask, currentMask, CV_GRAY2BGR);
269     }
270     else if(mask.channels()==3)
271     {
272         currentMask = mask;
273     }
274     
275     maskGaussPyramidImgs.push_back(currentMask);
276     for (int k = 1; k < levels+1; k++)
277     {
278         cv::Mat downMask;
279         if (k <