双线性内插原理:
由于图像数据组成为等间距横向和纵向分布的像素点构成,点与点之间间距为一个像素,那么对图像的内插,就可以当做在每个像素内横向和纵向均匀增加点,每个点的像素值则由周围四个点确定。
假设当点P(x,y)位于P1(x1,y1)、P2(x2,y2)、P3(x3,y3)和P4(x4,y4)四个点构成的矩阵内,如图所示:
设定四个点像素值为p1、p2、p3、p4,中间P点像素为p,P点像素值通过P1-P4四个点像素进行拟合。这里采用距离加权的方法对内插点像素进行赋值,即通过计算P点坐标与其他几个点距离来确定每个值所占权重,假设P点刚好位于矩阵中心,那么p刚好等于p1-p4的均值。
问题的核心在于如何计算P点与周围四个点距离,由于P点在矩阵内,假设一个矩阵边长为1,x和y与周围点坐标的差值都小于等于1,那么可以通过如下公式来计算p点像素值:
p=p1 * [(1 - |x - x1|) * (1 - |y - y1|)] + p2 * [(1 - |x - x2|) * (1 - |y - y2|)] + p3 * [(1 - |x - x3|) * (1 - |y-y3|)] + p4 * [(1 -|x - x4|) * (1 - |y-y4|)]
通过以上公式,当P点位于矩阵中心时,x-x1=0.5,y-y1=0.5,P1点权重为0.5*0.5=0.25,P2到P4点权重也一样是0.25,如果P点刚好位于P1-P4点任意一点上,通过公式计算结果也是p1-p4。
代码:
double result = p1 * ((1 - Math.abs(x - x1)) * (1 - Math.abs(y - y1)))
+ p2 * ((1 - Math.abs(x - x2)) * (1 - Math.abs(y - y2)))
+ p3 * ((1 - Math.abs(x - x3)) * (1 - Math.abs(y - y3)))
+ p4 * ((1 - Math.abs(x - x4)) * (1 - Math.abs(y - y4)));
假设P1 = 100.25, p2= 120.23, p3 = 103.49, p4 = 105.66,P点坐标为(0.2,0.3),计算结果p=112.217,四个点均值为107.4075,P点坐标最接近P2点,P2点最高,因此P点像素大于均值也更符合预期。
图像双线性内插的实现:
- 读取原图数据,获取原图宽高XY,设定内插数n;
- 根据原图宽高生成目标图像宽高,这里要注意的是,由于双线性内插必须在原图X和Y方向内部都有值得地方进行,那么原图X方向边缘像素和Y方向最边缘像素无法进行内插,那么目标图像宽和高的计算应该是(X-1)*n,(Y-1)*n;
- 采用双线性内插对目标图像像素进行赋值,目标图像任意像素(x,y),当x/n,y/n必定落在原图四个像素组成的区间内,(x,y)值,就是原图中四个像素值进行双线性内插计算结果。
测试代码:
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
/**
* 双线性内插
*
* @author chen
*
*/
public class BilinearInterpolation {
public void interpolation(File inputFile, File destFile, int interpolation) throws Exception {
BufferedImage inImage = ImageIO.read(inputFile);
int width = inImage.getWidth();
int height = inImage.getHeight();
int destWidth = (width - 1) * interpolation;
int destHeight = (height - 1) * interpolation;
destFile.createNewFile();
BufferedImage destImage = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < destWidth; i++) {
for (int j = 0; j < destHeight; j++) {
double x = (double) i / (double) interpolation;
double y = (double) j / (double) interpolation;
int x1 = (int) x;
int x2 = x1;
int x3 = x1 + 1;
int x4 = x3;
int y1 = (int) y + 1;
int y2 = y1 - 1;
int y3 = y2;
int y4 = y1;
int p1 = inImage.getRGB(x1, y1);
int p2 = inImage.getRGB(x2, y2);
int p3 = inImage.getRGB(x3, y3);
int p4 = inImage.getRGB(x4, y4);
int R = caculateValue(x, y, x1, x2, x3, x4, y1, y2, y3, y4, (p1 >> 16) & 0xff, (p2 >> 16) & 0xff,
(p3 >> 16) & 0xff, (p4 >> 16) & 0xff);
int G = caculateValue(x, y, x1, x2, x3, x4, y1, y2, y3, y4, (p1 >> 8) & 0xff, (p2 >> 8) & 0xff,
(p3 >> 8) & 0xff, (p4 >> 8) & 0xff);
int B = caculateValue(x, y, x1, x2, x3, x4, y1, y2, y3, y4, p1 & 0xff, p2 & 0xff, p3 & 0xff,
p4 & 0xff);
int rgb = (255 & 0xff) << 24 | (R & 0xff) << 16 | ( G & 0xff) << 8
| ( B & 0xff);
destImage.setRGB(i, j, rgb);
}
}
ImageIO.write(destImage, "JPG", destFile);
}
private int caculateValue(double x, double y, int x1, int x2, int x3, int x4, int y1, int y2, int y3, int y4,
double p1, double p2, double p3, double p4) {
int result = (int) (p1 * ((1 - Math.abs(x - x1)) * (1 - Math.abs(y - y1)))
+ p2 * ((1 - Math.abs(x - x2)) * (1 - Math.abs(y - y2)))
+ p3 * ((1 - Math.abs(x - x3)) * (1 - Math.abs(y - y3)))
+ p4 * ((1 - Math.abs(x - x4)) * (1 - Math.abs(y - y4))));
return result;
}
public static void main(String[] args) throws Exception {
new BilinearInterpolation().interpolation(new File("C:\\Users\\admin\\Desktop\\test\\1.png"),
new File("C:\\Users\\admin\\Desktop\\test\\11.jpg"), 10);
}
}