圖像處理之Lanczos采樣放縮算法
一:什麼是Lanczos采樣
參見這裡:http://en.wikipedia.org/wiki/Lanczos_resampling
二:大緻算法流程

三:算法運作結果
1.向下采樣, 生成縮略圖, 左邊為原圖,右邊為縮略圖
向上采樣,生成放大圖像時效果:
算法源代碼:
package com.gloomyfish.zoom.study;
import java.awt.image.BufferedImage;
import java.awt.image.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 image
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.