好久之前,当我第一次看到这个算法时候,我就爱上它了,那个时候我不懂什么是高

斯金字塔,但是我知道埃及有金字塔。一番研究之后,搞懂了什么是图像金字塔于是

我写了一篇文章在我的博客上,可以看这里:

http://blog.csdn.net/jia20003/article/details/9116931

但是金字塔有什么应用呢,可能最广泛的一个应用就是实现图像融合和图像的无缝

拼接于是我决定在研究一番,于是就有了这篇文章。好了不废话了。算法需要三张

图片,两张图片是需要拼接的图片,最后一张是面罩图片,为什么需要后面我会解释

面罩图片就是选取待拼接两张图片的目标边缘部分,多少合适根据需要。Demo演示

我是各占原图的1/2这样省事。

算法大致的步骤可以分为如下几步:

1.对两张待拼接的图片分别生成DOG,关于什么是DOG,怎么生成,如果不知道

一定要看看这里:

2.对面罩图片(mask image)完成高斯金字塔,层数要跟DOG层数相同。

3. 根据面罩图片的权重,拼接两张图片的DOG,生成一个DOG图片

4.用生成的DOG图片与maskimage 金字塔expand生成的图片相加得到每层,把每一次

叠加得到最后输出图片。

基于高斯金字塔图像融合的原理

懂得高斯金字塔DOG的生成原理都明白,如果把金字塔reduce与expand的结果相减则

得到DOG,而如果把expand结果与DOG结果相加则得到reduce处理后的图像,因为

reduce图像是间隔采样生成原图,而高斯金字塔融合正是巧妙的利用了这点。

关键代码解释:

实现目标图像DOG提取代码如下,默认情况下是三层:

PyramidBlendProcessor pyramid = new PyramidBlendProcessor(image1, image2, maskImage); BufferedImage[] image1Lapls = pyramid.getLaplacianPyramid(pyramid.pyramidDown(image1)); BufferedImage[] image2Lapls = pyramid.getLaplacianPyramid(pyramid.pyramidDown(image2)); BufferedImage[] maskPyramid = pyramid.pyramidDown(maskImage);

依靠mask权重实现两个目标图像DOG按层融合的代码如下:

	public BufferedImage blendOneImage(BufferedImage image1, BufferedImage image2, BufferedImage maskImage, BufferedImage blendedImage) { 		int width = image1.getWidth();         int height = image1.getHeight();          if ( blendedImage == null )         	blendedImage = createCompatibleDestImage( maskImage, null );          int[] image1Pixels = new int[width*height];         int[] image2Pixels = new int[width*height];         int[] maskPixels = new int[width*height];         int[] outPixels = new int[width*height];         getRGB( image1, 0, 0, width, height, image1Pixels );         getRGB( image2, 0, 0, width, height, image2Pixels );         getRGB( maskImage, 0, 0, width, height, maskPixels );         int index = 0;         float mr = 0, mg = 0, mb = 0;         for(int row=0; row> 24) & 0xff;                 tr1 = (image1Pixels[index] >> 16) & 0xff;                 tg1 = (image1Pixels[index] >> 8) & 0xff;                 tb1 = image1Pixels[index] & 0xff;                          		ta2 = (image2Pixels[index] >> 24) & 0xff;                 tr2 = (image2Pixels[index] >> 16) & 0xff;                 tg2 = (image2Pixels[index] >> 8) & 0xff;                 tb2 = image2Pixels[index] & 0xff;                          		ta3 = (maskPixels[index] >> 24) & 0xff;                 tr3 = (maskPixels[index] >> 16) & 0xff;                 tg3 = (maskPixels[index] >> 8) & 0xff;                 tb3 = maskPixels[index] & 0xff;                                  mr = tr3 / 255.0f;                 mg = tg3 / 255.0f;                 mb = tb3 / 255.0f;                 int br = (int)(mr * tr2  +  (1.0f - mr) * tr1);                 int bg = (int)(mg * tg2  +  (1.0f - mr) * tg1);                 int bb = (int)(mb * tb2  +  (1.0f - mr) * tb1);                 outPixels[index] = (ta1 << 24) | (clamp(br) << 16) | (clamp(bg) << 8) | clamp(bb);         	}         }         setRGB( blendedImage, 0, 0, width, height, outPixels );         return blendedImage; 	}
合并DOG融合每层图片与mask expand之后的代码如下:

		BufferedImage[] image1Lapls = this.pyramidUp(this.pyramidDown(maskImg)); 		BufferedImage result = null; 		int size = blendResults.length; 		for(int i=size - 1; i>=0; i--) 		{ 			if((i-1) < 0){ 				result = this.collapse(image1Lapls[i], blendResults[i]); 			} else { 				image1Lapls[i-1] = this.pyramidExpand(this.collapse(image1Lapls[i], blendResults[i]), image1Lapls[i-1].getWidth(), image1Lapls[i-1].getHeight());	 			} 		} 		// return image1Lapls[0]; 		return result;
图片一:

图像处理之基于高斯金字塔的图像融合_第1张图片

图片二:

图像处理之基于高斯金字塔的图像融合_第2张图片

mask图片:一半是黑色一半是白色

图像处理之基于高斯金字塔的图像融合_第3张图片

最终效果如下:

图像处理之基于高斯金字塔的图像融合_第4张图片

完全源代码:

package com.gloomyfish.image.pyramid.blend;  import java.awt.image.BufferedImage;  import com.gloomyfish.image.pyramid.PyramidAlgorithm;   public class PyramidBlendProcessor extends PyramidAlgorithm { 	BufferedImage image1; 	BufferedImage image2;  	BufferedImage maskImg;   	public PyramidBlendProcessor(BufferedImage image1, BufferedImage image2, BufferedImage maskImage) 	{ 		this.image1 = image1; 		this.image2 = image2; 		this.maskImg = createMaskImage(image1, image2, maskImage); 	} 	 	public BufferedImage mergeMaskWithResult(BufferedImage[] blendResults) 	{ 		BufferedImage[] image1Lapls = this.pyramidUp(this.pyramidDown(maskImg)); 		BufferedImage result = null; 		int size = blendResults.length; 		for(int i=size - 1; i>=0; i--) 		{ 			if((i-1) < 0){ 				result = this.collapse(image1Lapls[i], blendResults[i]); 			} else { 				image1Lapls[i-1] = this.pyramidExpand(this.collapse(image1Lapls[i], blendResults[i]), image1Lapls[i-1].getWidth(), image1Lapls[i-1].getHeight());	 			} 		} 		// return image1Lapls[0]; 		return result; 	} 	 	private BufferedImage createMaskImage(BufferedImage image12, 			BufferedImage image22, BufferedImage maskImage) { 		int width = image1.getWidth();         int height = image1.getHeight();         BufferedImage realMaskImg = createCompatibleDestImage( image1, null );          int[] image1Pixels = new int[width*height];         int[] image2Pixels = new int[width*height];         int[] maskPixels = new int[width*height];         int[] outPixels = new int[width*height];         getRGB( image1, 0, 0, width, height, image1Pixels );         getRGB( image2, 0, 0, width, height, image2Pixels );         getRGB( maskImage, 0, 0, width, height, maskPixels );         int index = 0;         for(int row=0; row> 24) & 0xff;                 tr1 = (image1Pixels[index] >> 16) & 0xff;                 tg1 = (image1Pixels[index] >> 8) & 0xff;                 tb1 = image1Pixels[index] & 0xff;                          		ta2 = (image2Pixels[index] >> 24) & 0xff;                 tr2 = (image2Pixels[index] >> 16) & 0xff;                 tg2 = (image2Pixels[index] >> 8) & 0xff;                 tb2 = image2Pixels[index] & 0xff;                          		ma = (maskPixels[index] >> 24) & 0xff;                 mr = (maskPixels[index] >> 16) & 0xff;                 mg = (maskPixels[index] >> 8) & 0xff;                 mb = maskPixels[index] & 0xff;                 if(mr < 127) {                 	outPixels[index] = (ta1 << 24) | (tr1 << 16) | (tg1 << 8) | tb1;                 } else if(mr >=127) {                 	outPixels[index] = (ta2 << 24) | (tr2 << 16) | (tg2 << 8) | tb2;                 } else {                 	//outPixels[index] = (ta2 << 24) | (mr << 16) | (mg << 8) | mb;                 }                             	}         }         setRGB( realMaskImg, 0, 0, width, height, outPixels );         return realMaskImg; 	} }
PyramidAlgorithm代码参见这里:

http://blog.csdn.net/jia20003/article/details/9116931

UI部分的代码如下:

package com.gloomyfish.image.pyramid.blend;  import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException;  import javax.imageio.ImageIO; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel;  public class MainUI extends JComponent implements ActionListener {  	/** 	 *  	 */ 	private JButton blendBtn; 	private Dimension mySize; 	private BufferedImage resultImage; 	private BufferedImage maskImage; 	private BufferedImage image1; 	private BufferedImage image2; 	 	private static final long serialVersionUID = 1L; 	 	public MainUI(BufferedImage image1, BufferedImage image2, BufferedImage maskImage) 	{         JPanel btnPanel = new JPanel();         btnPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));         blendBtn = new JButton("Pyramid Blend");         blendBtn.addActionListener(this);         btnPanel.add(blendBtn);         mySize = new Dimension(600, 600);         this.image1 = image1;         this.image2 = image2;         this.maskImage = maskImage;         JFrame mainFrame = new JFrame("Pyramid Blend Demo - Gloomyfish");         mainFrame.getContentPane().setLayout(new BorderLayout());         mainFrame.getContentPane().add(this, BorderLayout.CENTER);         mainFrame.getContentPane().add(btnPanel, BorderLayout.SOUTH);         mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);           mainFrame.pack();           mainFrame.setVisible(true);   	} 	 	@Override 	public Dimension getPreferredSize() { 		return mySize; 	}  	@Override 	public void actionPerformed(ActionEvent e) { 		// get two DOG image 		PyramidBlendProcessor pyramid = new PyramidBlendProcessor(image1, image2, maskImage, 4); 		 		BufferedImage[] image1Lapls = pyramid.getLaplacianPyramid(pyramid.pyramidDown(image1)); 		BufferedImage[] image2Lapls = pyramid.getLaplacianPyramid(pyramid.pyramidDown(image2)); 		BufferedImage[] maskPyramid = pyramid.pyramidDown(maskImage); 		maskPyramid = pyramid.pyramidUp(maskPyramid); 		System.out.println("End first step......"); 		// get mask pyramid 		 		// blend them by level from top to bottom 		BufferedImage[] blendImages = new BufferedImage[image1Lapls.length]; 		for(int i=0; i0; i--) //		{ //			BufferedImage destImage = pyramid.pyramidExpand(blendImage[i], blendImage[i-1].getWidth(),  blendImage[i-1].getHeight()); //			cImages[i-1] = pyramid.collapse(destImage, blendImage[i-1]); //		} //		resultImage = cImages[0]; 		resultImage = pyramid.mergeMaskWithResult(blendImages); //		for(int i=cImages.length - 1; i>0; i--)  //		{ //			BufferedImage destImage = pyramid.pyramidExpand(cImages[i], cImages[i-1].getWidth(),  cImages[i-1].getHeight()); //			resultImage = pyramid.collapse(destImage, cImages[i-1]); //		} 		repaint(); 	}  	@Override 	protected void paintComponent(Graphics g) { 		if(resultImage != null) 		{ 			g.drawImage(resultImage, 10, 10, resultImage.getWidth(), resultImage.getHeight(), null); 		} 		super.paintComponent(g); 	} 	     public static void main(String[] args) {      	 		try { 			File f2 = new File("D:\\resource\\orange.jpg"); 			File f1 = new File("D:\\resource\\apple.jpg"); 			File f3 = new File("D:\\resource\\mask512.jpg"); 			BufferedImage image1 = ImageIO.read(f1); 			BufferedImage image2 = ImageIO.read(f2);  			BufferedImage maskImg = ImageIO.read(f3);  			new MainUI(image1, image2, maskImg);           } catch (IOException e1) {               e1.printStackTrace();           }       } 	  } 
代码写的比较乱,做的时候就是希望快点看到效果,可读性不是很好,可能需要

整一下代码,但是内容绝对值得一读,希望得到大家的支持。

层数越多,融合效果越好,当然计算时间也越长!

转载请务必注明,谢谢!