X方向的线性插值
对于标准的双线性差值算法,X方向的线性插值:
具体到我们所实现的算法中,我们使Q11、Q12、Q21、Q22为光栅上相邻的四点,即P只能落于这四点其中一点上。Δcol是当前像素离像素所属区域原点的水平距离,比如图2,各种不同的颜色代表一个区域,区域原点为区域左上角的像素。
δ (R2) =( Color (Q22) −Color (Q12)) ∙Δcol+Color (Q12) ∙256 (1)
δ (R1) =( Color (Q21) −Color (Q11)) ∙Δcol+Color (Q11) ∙256 (2)
其中:Δcol=(DestColNumber∙((SrcWidth≪8)/DestWidth))&255, Color(X)表示点X的颜色,具体算法使用的是24位真彩色格式。
Y方向的线性插值
做完X方向的插值后再做Y方向的插值,对于一般情况,有:
而我们的具体算法中,Y方向的线性插值方法如(3)所示。Δrow是当前像素离像素所属区域原点的垂直距离,比如图2,各种不同的颜色代表一个区域,区域原点为区域左上角的像素。
Color (P )= (δ (R2) ∙256+ (δ (R2) −δ (R1) )∙Δrow )≫16 (3)
其中:Δrow=(DestRowNumber∙((SrcHeight≪8)/DestHeight))&255,由于前面为了便于计算左移了16位,因此最后需要右移16位保持匹配。
算法描述
for (目标图像第一行的像素++) { // 源图像上Q12, Q22, Q11, Q21的选取见下一节 获取源图像Q12, Q22, Q11, Q21的颜色; // X 方向的插值 δ(R2) = (Color(Q22) - Color(Q12)) * Δcol+ Color(Q12) * 256; δ(R1) = (Color(Q21) - Color(Q11)) * Δcol+ Color(Q11) * 256; // 保存 δ(R1)到一个临时数组,因为下一行的δ(R2)等于这一行的δ(R1) temp[i++] = δ(R1); // Y 方向的插值 Color(P) = (δ(R2) * 256 + (δ(R2) - δ(R1)) *Δrow) >> 16; 将 P 输出到目标位图中。 } for (目标图像第二行到最末行) { for (行上的像素++) { // 源图像上Q12, Q22, Q11, Q21的选取见下一节 获取源图像Q12, Q22, Q11, Q21的颜色; // X 方向的插值 δ(R2) = temp[i++]; // 下一行的δ(R2)等于上一行的δ(R1) δ(R1) = (Color(Q21) - Color(Q11)) *Δcol+ Color(Q11) * 256; // 保存 δ(R1)到一个临时数组,因为下一行的δ(R2)等于这一行的δ(R1) temp[i++] = δ(R1); // Y 方向的插值 Color(P) = (δ(R2) * 256 + (δ(R2) - δ(R1)) * Δrow) >> 16; 将 P 输出到目标位图中。 } }
算法中Q12, Q22, Q11, Q21的选取
我们以放大两倍为例,说明选取Q12, Q22, Q11, Q21的过程。源图像3*3区域放大为目标区域6*6区域。设以下为目标图像:
目标图像A像素区域对应的Q21,Q22,Q11,Q12,以红色区域为原点向右下方扩展的2*2区域。
目标图像B像素区域对应的Q21,Q22,Q11,Q12,以蓝色区域为原点向右下方扩展的2*2区域。
目标图像C像素区域对应的Q21,Q22,Q11,Q12,以绿色区域为原点向右下方扩展的2*2区域。
目标图像D像素区域对应的Q21,Q22,Q11,Q12,目标图像处于最后两行的边界情况,将Q21,Q22,Q11,Q12这四个点的值设为一样。
程序流程图
流程图右边虚线框中为相关过程的注解。
通俗的解释:
/** * <p>Title: </p> * <p>Description: 双线性插值缩放图片</p> * <p>Copyright: Copyright (c) 2010</p> * <p>Company: </p>*/ public class ZoomPixel { public static int interpolation(int[] imageData, int width, int heigth, float x, float y) { // 四个最临近象素的坐标(i1, j1), (i2, j1), (i1, j2), (i2, j2) int i1, i2; int j1, j2; int f1, f2, f3, f4; // 四个最临近象素值 int f12, f34; // 二个插值中间值 f1 = f2 = f3 = f4 = 0; // 定义一个值,当象素坐标相差小于改值时认为坐标相同 int EXP = 0; // 计算四个最临近象素的坐标 i1 = (int) x; i2 = i1 + 1; j1 = (int) y; j2 = j1 + 1; // 根据不同情况分别处理 if ((x < 0) || (x > width - 1) || (y < 0) || (y > heigth - 1)) { return 0x00ffffff; // 要计算的点不在源图范围内,直接返回255。 } else { if (Math.abs(x - width + 1) <= EXP) { // 要计算的点在图像右边缘上 if (Math.abs(y - heigth + 1) <= EXP) { // 要计算的点正好是图像最右下角那一个象素,直接返回该点象素值 f1 = (int)imageData[width * j1 + i1]; return (int)f1; } else { // 在图像右边缘上且不是最后一点,直接一次插值即可 f1 = (int)imageData[width * j1 + i1]; f3 = (int)imageData[width * j1 + i2]; // 返回插值结果 return (int) (f1 + (y - j1) * (f3 - f1)); } } else if (Math.abs(y - heigth + 1) <= EXP) { // 要计算的点在图像下边缘上且不是最后一点,直接一次插值即可 f1 = (int)imageData[width * j1 + i1]; f2 = (int)imageData[width * j2 + i1]; // 返回插值结果 return (int) (f1 + (x - i1) * (f2 - f1)); } else { // 计算四个最临近象素值 f1 = imageData[width * j1 + i1]; f2 = imageData[width * j1 + i2]; f3 = imageData[width * j2 + i1]; f4 = imageData[width * j2 + i2]; // 插值1 f12 = (int)(f1 + (x - i1) * (f2 - f1)); // 插值2 f34 = (int)((f3 + (x - i1) * (f4 - f3))); // 插值3 return (int)(f12 + (y - j1) * (f34 - f12)); } } } }
to use:
try { Image src = Image.createImage("/image.png"); int destW = 400; //缩放后图片的宽度 int destH = 400; //缩放图片的高度 int srcW = src.getWidth(); int srcH = src.getHeight(); // create pixel arrays int[] destPixels = new int[destW * destH]; // array to hold destination int[] destRed = new int[destW * destH]; int[] destGreen = new int[destW * destH]; int[] destBlue = new int[destW * destH]; // pixels int[] srcPixels = new int[srcW * srcH]; // array with source's pixels int[] srcRed = new int[srcW * srcH]; int[] srcGreen = new int[srcW * srcH]; int[] srcBlue = new int[srcW * srcH]; src.getRGB(srcPixels, 0, srcW, 0, 0, srcW, srcH); for (int i = 0; i < srcPixels.length; i++) { srcBlue[i] = srcPixels[i]&0x000000ff; srcGreen[i] = (srcPixels[i]>>8)&0x000000ff; srcRed[i] = (srcPixels[i]>>16)&0x000000ff; } // simple point smapled resizing // loop through the destination pixels, find the matching pixel on // the source and use that for (int destY = 0; destY < destH; ++destY) { for (int destX = 0; destX < destW; ++destX) { float srcX = ((float)destX * (float)srcW) / (float)destW; float srcY = ((float)destY * (float)srcH) / (float)destH; try { destRed[destX + destY * destW] = ZoomPixel.interpolation(srcRed, srcW, srcH, srcX, srcY); destGreen[destX + destY * destW] = ZoomPixel.interpolation(srcGreen, srcW, srcH, srcX, srcY); destBlue[destX + destY * destW] = ZoomPixel.interpolation(srcBlue, srcW, srcH, srcX, srcY); int value = 0xff000000; value|=destBlue[destX + destY * destW]; value|=(destGreen[destX + destY * destW]<<8); value|=(destRed[destX + destY * destW]<<16); destPixels[destX + destY * destW] = value; } catch (Exception e) { e.printStackTrace(); } } } } catch (Exception e) { e.printStackTrace(); }