双线性插值算法
图像的双线性插值放大算法中,目标图像中新创造的象素值,是由源图像位
置在它附近的 2*2 区域 4 个邻近象素的值通过加权平均计算得出的。双线性内插
值算法放大后的图像质量较高,不会出现像素值不连续的的情况。然而次算法具
有低通滤波器的性质,使高频分量受损,所以可能会使图像轮廓在一定程度上变
得模糊。
图 1
X 方向的线性插值
对于标准的双线性差值算法,X 方向的线性插值:
[通用 1]
[通用 2]
具体到我们所实现的算法中,我们使 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 方向的插值,对于一般情况,有:
[通用 3]
而我们的具体算法中,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
A
A
A
B
B
C
C
B
B
C
C
D
D
D
D
图 2
目标图像 A 像素区域对应的 Q21,Q22,Q11,Q12,以红色区域为原点向右下
方扩展的 2*2 区域。
Q21 Q22
Q11 Q12
图 3
目标图像 B 像素区域对应的 Q21,Q22,Q11,Q12,以蓝色区域为原点向右下
方扩展的 2*2 区域。
Q21 Q22
Q11 Q12
图 4
目标图像 C 像素区域对应的 Q21,Q22,Q11,Q12,以绿色区域为原点向右下
方扩展的 2*2 区域。
Q21 Q22
Q11 Q12
图 5
目标图像 D 像素区域对应的 Q21,Q22,Q11,Q12,目标图像处于最后两行的
边界情况,将 Q21,Q22,Q11,Q12 这四个点的值设为一样。
Q11=Q12=Q22=Q21
图 6
程序流程图
流程图右边虚线框中为相关过程的注解。
输入目标位图
的高度和宽度
创建目标位图
计 算 放 大 / 缩
小比例
计算 Q21 的列
号
计算 Q21 位置
获取 Q21 的颜
色
Q21 未落在
源图像末行
Y
N
scaleH = (原图像宽度 << 8) / 目标
图像宽度;
scaleV = (原图像高度 << 8) / 目标
图像高度;
scaleH 为水平比例,scaleV 为垂直比
例。
PixelColAddr = 目标图像像素列号
* scaleH;
ColDelta = PixelColAddr & 255;
Q21 的 列 号 = (PixelColAddr -
ColDelta) >> 8;
这样即可巧妙地根据不同的放大/缩
小比例计算出 Q21 的列号。
PixelColAddr 反应了目标位图上当
前像素离图像原点的水平偏离度。
ColDelta 反应了目标位图上当前像
素离区域原点的水平偏离度。
获取 Q11、Q12、
Q22 颜色
Q11 、 Q12 、 Q22
等于 Q21 的颜色
X 方向插值
Y 方向插值
将 像 素 值 输 出
到目标位图
下一个像素
Y
首 行 像 素
未到末尾
N
InterQ21andQ22 = ColDelta * (Q22
颜色 – Q21 颜色) + Q21 颜色 * 256;
InterQ11andQ12 = ColDelta * (Q12
颜色 – Q11 颜色) + Q11 颜色 * 256;
InterTemp[i++] = InterQ11andQ12;
对于真彩色位图,应该分别对每个像
素的 G、B、R 的值进行处理。
目标像素的值 = (InterQ21andQ22 *
256
+
(InterQ11andQ12
-
InterQ21andQ22) * RowDelta) >> 16;
对于真彩色位图,应该分别对每个像
素的 G、B、R 的值进行处理。
RowDelta 反应了目标位图上当前像
素离区域原点的垂直偏离度。
计算 Q21 行号
计算 Q21 列号
计算 Q21 位置
获取 Q21 的颜
色
Q21 未落在
源图像末行
Y
N
获取 Q11、Q12、
Q22 颜色
Q11 、 Q12 、 Q22
等于 Q21 的颜色
X 方向插值
Y 方向插值
将 像 素 值 输 出
到目标位图
下一个像素
Y
Y
目 标 图 像
未到列末
N
目 标 图 像
未到行末
i = 0;
PixelRowAddr =目标图像像素行号*
scaleV;
RowDelta = PixelRowAddr & 255;
Q21 的 行 号 = (PixelRowAddr -
RowDelta) >> 8;
这样即可巧妙地根据不同的放大/缩
小比例计算出 Q21 的行号。
辅助数组 InterTemp 的下标 i 归零。
PixelRowAddr 反应了目标位图上当
前像素离图像原点垂直偏离度。
RowDelta 反应了目标位图上当前像
素离区域原点的垂直偏离度。
PixelColAddr = 目标图像像素列号
* scaleH;
ColDelta = PixelColAddr & 255;
Q21 的 列 号 = (PixelColAddr -
ColDelta) >> 8;
InterQ21andQ22 = InterTemp[i];
InterQ11andQ12 = ColDelta * (Q12
颜色 – Q11 颜色) + Q11 颜色 * 256;
InterTemp[i++] = InterQ11andQ12;
当前行的 InterQ21andQ22 等于上一
行 的 InterQ11andQ12 , 因 此 用 到
InterTemp 辅助数组。。
对于真彩色位图,应该分别对每个像
素的 G、B、R 的值进行处理。
目标像素的值 = (InterQ21andQ22 *
256
+
(InterQ11andQ12
-
InterQ21andQ22) * RowDelta) >> 16;
对于真彩色位图,应该分别对每个像
素的 G、B、R 的值进行处理。
N
终止
双线性插值图像放大/缩小算法
void ResizeWorkingBitmap(tWorkBMP *a, tWorkBMP *b, WORD bx, WORD by)
{
unsigned int PtAR = 0, PtBR = 0, PtCR = 0, PtDR = 0, PixelValueR = 0;
unsigned int PtAG = 0, PtBG = 0, PtCG = 0, PtDG = 0, PixelValueG = 0;
unsigned int PtAB = 0, PtBB = 0, PtCB = 0, PtDB = 0, PixelValueB = 0;
register unsigned SpixelColNum = 0, SpixelRowNum = 0, DestCol = 0, DestRow = 0;
unsigned int SpixelColAddr = 0, SpixelRowAddr = 0;
unsigned int ColDelta = 0, RowDelta = 0, scaleV = 0, scaleH = 0;
unsigned int ContribAandBR = 0, ContribCandDR = 0;
unsigned int ContribAandBG = 0, ContribCandDG = 0;
unsigned int ContribAandBB = 0, ContribCandDB = 0;
unsigned int ContribTem[2048 * 3];// Max width is 2048
int i = 0;
CreateWorkingBitmap(bx, by, b);
// Calculation of zoom proportion
scaleH = (a->x << 8) / bx;
scaleV = (a->y << 8) / by;
// First line of destination image
for (DestCol = 0; DestCol < bx; DestCol++)
{
// Horizontal distance between origin of image and current pixel
SpixelColAddr = DestCol * scaleH;
// Horizontal distance between A and current pixel
ColDelta = SpixelColAddr & 255;
// Column number of A
SpixelColNum = (SpixelColAddr - ColDelta) >> 8;
// Get color of A
PtAB = a->b[3 * SpixelRowNum * a->x + 3 * SpixelColNum];
PtAG = a->b[3 * SpixelRowNum * a->x + 3 * SpixelColNum + 1];
PtAR = a->b[3 * SpixelRowNum * a->x + 3 * SpixelColNum + 2];
// Get color of B, C, D
if ((SpixelColNum + 1) < a->x)
{
PtBB = a->b[3 * SpixelRowNum * a->x + 3 * (SpixelColNum + 1)];
PtBG = a->b[3 * SpixelRowNum * a->x + 3 * (SpixelColNum+1) + 1];
PtBR = a->b[3 * SpixelRowNum * a->x + 3 * (SpixelColNum+1) + 2];
PtCB = a->b[3 * (SpixelRowNum+1) * a->x + 3 * SpixelColNum];
PtCG = a->b[3 * (SpixelRowNum+1) * a->x + 3 * SpixelColNum + 1];
PtCR = a->b[3 * (SpixelRowNum+1) * a->x + 3 * SpixelColNum + 2];
PtDB = a->b[3 * (SpixelRowNum + 1) * a->x + 3 * (SpixelColNum + 1)];
PtDG = a->b[3 * (SpixelRowNum+1) * a->x + 3 * (SpixelColNum+1) + 1];
PtDR = a->b[3 * (SpixelRowNum+1) * a->x + 3 * (SpixelColNum+1) + 2];
}
else
{
}
PtBB = PtCB = PtDB = PtAB;
PtBG = PtCG = PtDG = PtAG;
PtBR = PtCR = PtDR = PtAR;
// X-direction interpolation of blue
ContribAandBB = ColDelta * (PtBB - PtAB) + PtAB * 256;
ContribCandDB = ColDelta * (PtDB - PtCB) + PtCB * 256;
ContribTem[i++] = ContribCandDB;
// Y-direction interpolation of blue
PixelValueB = (ContribAandBB * 256 + (ContribCandDB - ContribAandBB) * RowDelta) >>
// X-direction interpolation of green
ContribAandBG = ColDelta * (PtBG - PtAG) + PtAG * 256;
ContribCandDG = ColDelta * (PtDG - PtCG) + PtCG * 256;
ContribTem[i++] = ContribCandDG;
// Y-direction interpolation of green
PixelValueG = (ContribAandBG * 256 + (ContribCandDG - ContribAandBG) * RowDelta) >>
// X-direction interpolation of red
ContribAandBR = ColDelta * (PtBR - PtAR) + PtAR * 256;
ContribCandDR = ColDelta * (PtDR - PtCR) + PtCR * 256;
ContribTem[i++] = ContribCandDR;
// Y-direction interpolation of red
PixelValueR = (ContribAandBR * 256 + (ContribCandDR - ContribAandBR) * RowDelta) >>
// Output current pixel to destination image
b->b[3 * DestRow * bx + 3 * DestCol] = PixelValueB;
b->b[3 * DestRow * bx + 3 * DestCol + 1] = PixelValueG;
b->b[3 * DestRow * bx + 3 * DestCol + 2] = PixelValueR;
16;
16;
16;
}