图像处理之Lanczos采样放缩算法

一:什么是Lanczos采样

参见这里:http://en.wikipedia.org/wiki/Lanczos_resampling

二:大致算法流程

20140105002704718

三:算法运行结果

1.向下采样, 生成缩略图, 左边为原图,右边为缩略图

图像处理之Lanczos采样放缩算法_第1张图片

向上采样,生成放大图像时效果:

图像处理之Lanczos采样放缩算法_第2张图片

算法源代码:

package com.gloomyfish.zoom.study; 
    
import java.awt.p_w_picpath.BufferedImage; 
import java.awt.p_w_picpath.ColorModel; 
    
import com.gloomyfish.filter.study.AbstractBufferedImageOp; 
    
public class LanczosScaleFilter extends AbstractBufferedImageOp { 
    // lanczos_size 
    private float lanczosSize; 
    private float destWidth; 
        
    public LanczosScaleFilter() 
    { 
        lanczosSize = 3; 
        destWidth = 100; 
    } 
    
    public LanczosScaleFilter(float lobes, int width) { 
        this.lanczosSize = lobes; 
        this.destWidth = width; 
    } 
    
    public void setLanczosSize(float size) { 
        this.lanczosSize = size; 
    } 
    
    public void setDestWidth(float destWidth) { 
        this.destWidth = destWidth; 
    } 
    
    @Override 
    public BufferedImage filter(BufferedImage src, BufferedImage dest) { 
        int width = src.getWidth(); 
        int height = src.getHeight(); 
        float ratio = width / this.destWidth; 
        float rcp_ratio = 2.0f / ratio; 
        float range2 = (float) Math.ceil(ratio * lanczosSize / 2); 
            
        // destination p_w_picpath 
        int dh = (int)(height * (this.destWidth/width)); 
        int dw = (int)this.destWidth; 
    
        if (dest == null) { 
            ColorModel cMD = src.getColorModel(); 
            dest = new BufferedImage(src.getColorModel(), cMD.createCompatibleWritableRaster(dw, dh), cMD.isAlphaPremultiplied(), null); 
        } 
    
        int[] inPixels = new int[width * height]; 
        int[] outPixels = new int[dw * dh]; 
            
        getRGB(src, 0, 0, width, height, inPixels); 
        int index = 0; 
        float fcy = 0, icy = 0, fcx = 0, icx = 0; 
        for (int row = 0; row < dh; row++) { 
            int ta = 0, tr = 0, tg = 0, tb = 0; 
            fcy = (row + 0.5f) * ratio; 
            icy = (float) Math.floor(fcy); 
            for (int col = 0; col < dw; col++) { 
                fcx = (col + 0.5f) * ratio; 
                icx = (float) Math.floor(fcx); 
    
                float sumred = 0, sumgreen = 0, sumblue = 0; 
                float totalWeight = 0; 
                for (int subcol = (int) (icx - range2); subcol <= icx + range2; subcol++) { 
                    if (subcol < 0 || subcol >= width) 
                        continue; 
                    int ncol = (int) Math.floor(1000 * Math.abs(subcol - fcx)); 
    
                    for (int subrow = (int) (icy - range2); subrow <= icy + range2; subrow++) { 
                        if (subrow < 0 || subrow >= height) 
                            continue; 
                        int nrow = (int) Math.floor(1000 * Math.abs(subrow - fcy)); 
                        float weight = (float) getLanczosFactor(Math.sqrt(Math.pow(ncol * rcp_ratio, 2) 
                                + Math.pow(nrow * rcp_ratio, 2)) / 1000); 
                        if (weight > 0) { 
                            index = (subrow * width + subcol); 
                            tr = (inPixels[index] >> 16) & 0xff; 
                            tg = (inPixels[index] >> 8) & 0xff; 
                            tb = inPixels[index] & 0xff; 
                            totalWeight += weight; 
                            sumred += weight * tr; 
                            sumgreen += weight * tg; 
                            sumblue += weight * tb; 
                        } 
                    } 
                } 
                index = row * dw + col; 
                tr = (int) (sumred / totalWeight); 
                tg = (int) (sumgreen / totalWeight); 
                tb = (int) (sumblue / totalWeight); 
                outPixels[index] = (255 << 24) | (clamp(tr) << 16) | (clamp(tg) << 8) | clamp(tb); 
                    
                // clear for next pixel 
                sumred = 0; 
                sumgreen = 0; 
                sumblue = 0; 
                totalWeight = 0; 
    
            } 
        } 
        setRGB(dest, 0, 0, dw, dh, outPixels); 
        return dest; 
    } 
        
    public static int clamp(int v) 
    { 
        return v > 255 ? 255 : (v < 0 ? 0 : v); 
    } 
    
    private double getLanczosFactor(double distance) { 
        if (distance > lanczosSize) 
            return 0; 
        distance *= Math.PI; 
        if (Math.abs(distance) < 1e-16) 
            return 1; 
        double xx = distance / lanczosSize; 
        return Math.sin(distance) * Math.sin(xx) / distance / xx; 
    } 
    
}

五:窗口大小对结果的影响

如果是向下采样生成缩略图的话, 窗口大小设置为3就已经非常清楚了

如果向上采样要放大图像的话, 窗口大小设置要大于6才能获得较好结果,推荐使用窗口

大小为8.