zoukankan      html  css  js  c++  java
  • 图像处理之基于高斯金字塔的图像融合

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

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

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

    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<height; row++) {
            	int ta1 = 0, tr1 = 0, tg1 = 0, tb1 = 0;
            	int ta2 = 0, tr2 = 0, tg2 = 0, tb2 = 0;
            	int ta3 = 0, tr3 = 0, tg3 = 0, tb3 = 0;
            	for(int col=0; col<width; col++) {
            		index = row * width + col;
            		ta1 = (image1Pixels[index] >> 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;

    图片一:


    图片二:


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


    最终效果如下:


    完全源代码:

    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<height; row++) {
            	int ta1 = 0, tr1 = 0, tg1 = 0, tb1 = 0;
            	int ta2 = 0, tr2 = 0, tg2 = 0, tb2 = 0;
            	int ma = 0, mr = 0, mg = 0, mb = 0;
            	for(int col=0; col<width; col++) {
            		index = row * width + col;
            		ta1 = (image1Pixels[index] >> 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; i<blendImages.length; i++)
    		{
    			blendImages[i] = pyramid.blendOneImage(image1Lapls[i], image2Lapls[i], maskPyramid[i], null);			
    		}
    		
    		// collapse them
    //		BufferedImage[] cImages = new BufferedImage[blendImage.length-1];
    //		for(int i=(blendImage.length-1); i>0; 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();  
            }  
        }
    	
    
    }
    

    代码写的比较乱,做的时候就是希望快点看到效果,可读性不是很好,可能需要

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

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

    转载请务必注明,谢谢!

  • 相关阅读:
    面试范围
    Spark集群基础概念 与 spark架构原理
    RDD 算子补充
    常用Actoin算子 与 内存管理 、共享变量、内存机制
    常用Transformation算子
    spark core (二)
    Spark 介绍
    JSON对象和string的相互转换
    javascript中substring和substr方法
    mongodb学习相关网址
  • 原文地址:https://www.cnblogs.com/james1207/p/3331374.html
Copyright © 2011-2022 走看看