图像处理之高斯金字塔

一:图像金字塔基本操作

对一张图像不断的模糊之后向下采样,得到不同分辨率的图像,同时每次得到的

新的图像宽与高是原来图像的1/2, 最常见就是基于高斯的模糊之后采样,得到的

一系列图像称为高斯金字塔。

图像处理之高斯金字塔_第1张图片

图像处理之高斯金字塔_第2张图片

图像处理之高斯金字塔_第3张图片

高斯金字塔不同(DoG)又称为拉普拉斯金字塔,其计算公式如下:

L(i) = G(i) – expand(G(i+1))

第i层拉普拉斯金字塔是由第i层高斯金字塔减去第i+1层高斯金字塔expand之后得到。

本文得到的DoG(Difference of Gaussian)结果如下:

图像处理之高斯金字塔_第4张图片

二:关键代码解析

金字塔reduce操作实现代码如下:

	private BufferedImage pyramidReduce(BufferedImage src) {
		int width = src.getWidth();
        int height = src.getHeight();
        BufferedImage dest = createSubCompatibleDestImage(src, null);
        int[] inPixels = new int[width*height];
        int ow = width/2;
        int oh = height/2;
        int[] outPixels = new int[ow*oh];
        getRGB(src, 0, 0, width, height, inPixels );
        int inRow=0, inCol = 0, index = 0, oudex =0, ta = 0;
        float[][] keneralData = this.getHVGaussianKeneral();
        for(int row=0; row= height) {
        			inRow = 0;
        		}
        		if(inCol >= width) {
        			inCol = 0;
        		}
        		float sumRed = 0, sumGreen = 0, sumBlue = 0;
        		for(int subRow = -2; subRow <= 2; subRow++) {
        			int inRowOff = inRow + subRow;
        			if(inRowOff >= height || inRowOff < 0) {
        				inRowOff = 0;
        			}
        			for(int subCol = -2; subCol <= 2; subCol++) {
        				int inColOff = inCol + subCol;
        				if(inColOff >= width || inColOff < 0) {
        					inColOff = 0;
        				}
        				index = inRowOff * width + inColOff;
        				ta = (inPixels[index] >> 24) & 0xff;
        				int red = (inPixels[index] >> 16) & 0xff;
        				int green = (inPixels[index] >> 8) & 0xff;
        				int blue = inPixels[index] & 0xff;
        				sumRed += keneralData[subRow + 2][subCol + 2] * red;
        				sumGreen += keneralData[subRow + 2][subCol + 2] * green;
        				sumBlue += keneralData[subRow + 2][subCol + 2] * blue;
        			}
        		}
        		
        		oudex = row * ow + col;
        		outPixels[oudex] = (ta << 24) | (clamp(sumRed) << 16) | (clamp(sumGreen) << 8) | clamp(sumBlue);
        	}
        }
        setRGB( dest, 0, 0, ow, oh, outPixels );
        return dest;
	}
金字塔expand实现代码如下:

public BufferedImage pyramidExpand(BufferedImage src) {
	int width = src.getWidth();
	int height = src.getHeight();
	int[] inPixels = new int[width*height];
	getRGB(src, 0, 0, width, height, inPixels );
	int ow = 2*width;
	int oh =2*height;
	int[] outPixels = new int[ow * oh];
	int index = 0, outdex = 0, ta = 0;
	float[][] keneralData = this.getHVGaussianKeneral();
	BufferedImage dest = createTwiceCompatibleDestImage(src, null);
	for(int row=0; row 0) {
    				continue;
    			}
    			if(srcRow >= height || srcRow < 0) {
    				srcRow = 0;
    			}
    			for(int subCol = -2; subCol <= 2; subCol++) {
    				double srcColOff = (col + subCol)/2.0;
    				j = Math.floor(srcColOff);
    				t = srcColOff - j;
    				if(t > 0) {
    					continue;
    				}
    				if(srcColOff >= width || srcColOff < 0) {
    					srcColOff = 0;
    				}
    				index = (int)(srcRow * width + srcColOff);
    				ta = (inPixels[index] >> 24) & 0xff;
    				int red = (inPixels[index] >> 16) & 0xff;
    				int green = (inPixels[index] >> 8) & 0xff;
    				int blue = inPixels[index] & 0xff;
    				sumRed += keneralData[subRow + 2][subCol + 2] * red;
    				sumGreen += keneralData[subRow + 2][subCol + 2] * green;
    				sumBlue += keneralData[subRow + 2][subCol + 2] * blue;
    			}
    		}
			outdex = row * ow + col;
    		outPixels[outdex] = (ta << 24) | (clamp(4.0f * sumRed) << 16) | (clamp(4.0f * sumGreen) << 8) | clamp(4.0f * sumBlue);
    		// outPixels[outdex] = (ta << 24) | (clamp(sumRed) << 16) | (clamp(sumGreen) << 8) | clamp(sumBlue);
		}
	}
	setRGB( dest, 0, 0, ow, oh, outPixels );
	return dest;
}
图像金字塔的reduce与expand过程都是卷积采样实现。特别注意的是 expand

操作不是reduce的可逆操作。

关于什么是卷积,高斯滤波请参见博客上的其它相关文章。

高斯金字塔全部算法源代码如下:

package com.gloomyfish.image.pyramid;

import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;

public class PyramidAlgorithm extends GaussianFilter {
	private float a;

	public PyramidAlgorithm() {
		a = 0.4f;
	}
	
	public void setParameter(float p) {
		this.a = p;
	}

	public BufferedImage[] pyramidDown(BufferedImage src, int level) {
		BufferedImage[] imagePyramids = new BufferedImage[level + 1];
		imagePyramids[0] = src;
		for(int i=1; i ewidth) ? ewidth : width;
	        height = (height > expandImage.getHeight()) ? expandImage.getHeight():height;
	        System.out.println(" width = " + width + " expand width = " + ewidth);
	        
	        int[] reducePixels = new int[width*height];
	        int[] expandPixels = new int[width*height];
	        int[] laPixels = new int[width*height];
	        getRGB( reduceImages[i-1], 0, 0, width, height, reducePixels);
	        getRGB( expandImage, 0, 0, width, height, expandPixels );
	        int index = 0;
	        int er = 0, eg = 0, eb = 0;
	        for(int row=0; row> 24) & 0xff;
	                tr = (reducePixels[index] >> 16) & 0xff;
	                tg = (reducePixels[index] >> 8) & 0xff;
	                tb = reducePixels[index] & 0xff;
	                
	        		ta = (expandPixels[index] >> 24) & 0xff;
	                er = (expandPixels[index] >> 16) & 0xff;
	                eg = (expandPixels[index] >> 8) & 0xff;
	                eb = expandPixels[index] & 0xff;
	                
	                tr = tr - er;
	                tg = tg - eg;
	                tb = tb - eb;
	                
	                laPixels[index] = (ta << 24) | (clamp(tr) << 16) | (clamp(tg) << 8) | clamp(tb);
	        	}
	        }
	        setRGB( laplaciImages[i-1], 0, 0, width, height, laPixels );
		}

        return laplaciImages;
	}
	
	private BufferedImage pyramidReduce(BufferedImage src) {
		int width = src.getWidth();
        int height = src.getHeight();
        BufferedImage dest = createSubCompatibleDestImage(src, null);
        int[] inPixels = new int[width*height];
        int ow = width/2;
        int oh = height/2;
        int[] outPixels = new int[ow*oh];
        getRGB(src, 0, 0, width, height, inPixels );
        int inRow=0, inCol = 0, index = 0, oudex =0, ta = 0;
        float[][] keneralData = this.getHVGaussianKeneral();
        for(int row=0; row= height) {
        			inRow = 0;
        		}
        		if(inCol >= width) {
        			inCol = 0;
        		}
        		float sumRed = 0, sumGreen = 0, sumBlue = 0;
        		for(int subRow = -2; subRow <= 2; subRow++) {
        			int inRowOff = inRow + subRow;
        			if(inRowOff >= height || inRowOff < 0) {
        				inRowOff = 0;
        			}
        			for(int subCol = -2; subCol <= 2; subCol++) {
        				int inColOff = inCol + subCol;
        				if(inColOff >= width || inColOff < 0) {
        					inColOff = 0;
        				}
        				index = inRowOff * width + inColOff;
        				ta = (inPixels[index] >> 24) & 0xff;
        				int red = (inPixels[index] >> 16) & 0xff;
        				int green = (inPixels[index] >> 8) & 0xff;
        				int blue = inPixels[index] & 0xff;
        				sumRed += keneralData[subRow + 2][subCol + 2] * red;
        				sumGreen += keneralData[subRow + 2][subCol + 2] * green;
        				sumBlue += keneralData[subRow + 2][subCol + 2] * blue;
        			}
        		}
        		
        		oudex = row * ow + col;
        		outPixels[oudex] = (ta << 24) | (clamp(sumRed) << 16) | (clamp(sumGreen) << 8) | clamp(sumBlue);
        	}
        }
        setRGB( dest, 0, 0, ow, oh, outPixels );
        return dest;
	}
	
    public BufferedImage createSubCompatibleDestImage(BufferedImage src, ColorModel dstCM) {
        if ( dstCM == null )
            dstCM = src.getColorModel();
        return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(src.getWidth()/2, src.getHeight()/2), dstCM.isAlphaPremultiplied(), null);
    }
    
    public BufferedImage createTwiceCompatibleDestImage(BufferedImage src, ColorModel dstCM) {
        if ( dstCM == null )
            dstCM = src.getColorModel();
        return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(src.getWidth()*2, src.getHeight()*2), dstCM.isAlphaPremultiplied(), null);
    }
	
	public BufferedImage pyramidExpand(BufferedImage src) {
		int width = src.getWidth();
		int height = src.getHeight();
		int[] inPixels = new int[width*height];
		getRGB(src, 0, 0, width, height, inPixels );
		int ow = 2*width;
		int oh =2*height;
		int[] outPixels = new int[ow * oh];
		int index = 0, outdex = 0, ta = 0;
		float[][] keneralData = this.getHVGaussianKeneral();
		BufferedImage dest = createTwiceCompatibleDestImage(src, null);
		for(int row=0; row 0) {
	    				continue;
	    			}
	    			if(srcRow >= height || srcRow < 0) {
	    				srcRow = 0;
	    			}
	    			for(int subCol = -2; subCol <= 2; subCol++) {
	    				double srcColOff = (col + subCol)/2.0;
	    				j = Math.floor(srcColOff);
	    				t = srcColOff - j;
	    				if(t > 0) {
	    					continue;
	    				}
	    				if(srcColOff >= width || srcColOff < 0) {
	    					srcColOff = 0;
	    				}
	    				index = (int)(srcRow * width + srcColOff);
	    				ta = (inPixels[index] >> 24) & 0xff;
	    				int red = (inPixels[index] >> 16) & 0xff;
	    				int green = (inPixels[index] >> 8) & 0xff;
	    				int blue = inPixels[index] & 0xff;
	    				sumRed += keneralData[subRow + 2][subCol + 2] * red;
	    				sumGreen += keneralData[subRow + 2][subCol + 2] * green;
	    				sumBlue += keneralData[subRow + 2][subCol + 2] * blue;
	    			}
	    		}
				outdex = row * ow + col;
	    		outPixels[outdex] = (ta << 24) | (clamp(4.0f * sumRed) << 16) | (clamp(4.0f * sumGreen) << 8) | clamp(4.0f * sumBlue);
	    		// outPixels[outdex] = (ta << 24) | (clamp(sumRed) << 16) | (clamp(sumGreen) << 8) | clamp(sumBlue);
			}
		}
		setRGB( dest, 0, 0, ow, oh, outPixels );
		return dest;
	}

}
特别注意:我没有处理像素的宽与高,如果宽与高不是偶数可能

会有问题,使用时请自己处理吧。

UI实现源代码如下:

package com.gloomyfish.image.pyramid;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.MediaTracker;
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.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class PyramidDemoUI extends JComponent implements ActionListener {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private JButton upButton;
	private JButton downButton;
	private BufferedImage[] reduceImages;
	private BufferedImage[] expandImages;
	private BufferedImage sourceImage;
	private Dimension mySize;
	private MediaTracker tracker;
	
	public PyramidDemoUI(File f)
	{
		initComponents(f);
	}

	private void initComponents(File f)
	{
		// TODO Auto-generated method stub
		try {  
			sourceImage = ImageIO.read(f);  
        } catch (IOException e1) {  
            e1.printStackTrace();  
        }  
          
        tracker = new MediaTracker(this);  
        tracker.addImage(sourceImage, 1);  
          
        // blocked 10 seconds to load the image data  
        try {  
            if (!tracker.waitForID(1, 10000)) {  
                System.out.println("Load error.");  
                System.exit(1);  
            }// end if  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
            System.exit(1);  
        }// end catch  
        
        JPanel btnPanel = new JPanel();
        btnPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
        upButton = new JButton("Laplacian Pyramid");
        downButton = new JButton("Pyramid Down");
        upButton.addActionListener(this);
        downButton.addActionListener(this);
        btnPanel.add(upButton);
        btnPanel.add(downButton);
        mySize = new Dimension(800, 800);   
        JFrame mainFrame = new JFrame("Pyramid 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
	protected void paintComponent(Graphics g) 
	{
//		g.drawImage(sourceImage, 10, 10, sourceImage.getWidth(), sourceImage.getHeight(), null);
		int width = 10;
//		if(reduceImages != null) {
//			for(int i=1; i
转载请务必注明出处-GloomyFish

你可能感兴趣的:(图像处理之高斯金字塔)