双线性插值算法的不足就是细节处理的不好,换句话说,就是曲线拟合得不够光滑,所以又有了双三次插值算法。双三次插值算法是基于周围的16个像素点,通过计算16个像素点的权重,累积得到增加点的像素值的。
简单点理解,公式如下:
p = f ( u , v ) = ∑ i = 0 3 ∑ j = 0 3 w i j x i y j p=f(u,v)=\sum_{i=0}^{3}\sum_{j=0}^{3}w_{ij}x_{i}y_{j} p=f(u,v)=i=0∑3j=0∑3wijxiyj
(u,v)为所插入点的坐标,(x,y)为周围16个像素点的坐标。本算法的重点和难点就是权重w的值如何获取。我们取其中的x方向(横轴)分析, w x i w_{xi} wxi的取值如下公式所示:
w x i = { ( a + 2 ) ∣ x i − u ∣ 3 − ( a + 3 ) ∣ x i − u ∣ 2 + 1 f o r ∣ x i − u ∣ ≤ 1 a ∣ x i − u ∣ 3 − 5 a ∣ x i − u ∣ 2 + 8 a ∣ x i − u ∣ − 4 a f o r 1 < ∣ x i − u ∣ < 2 0 o t h e r w i s e w_{xi}=\left\{\begin{matrix} (a+2)\left | x_i-u \right |^{3}-(a+3)\left | x_i-u \right |^{2}+1 & for & \left | x_i-u \right |\leq 1\\ a\left | x_i-u \right |^{3}-5a\left | x_i-u \right |^{2}+8a\left | x_i-u \right |-4a & for &1< \left | x_i-u \right |< 2 \\ 0 &otherwise \end{matrix}\right. wxi=⎩⎨⎧(a+2)∣xi−u∣3−(a+3)∣xi−u∣2+1a∣xi−u∣3−5a∣xi−u∣2+8a∣xi−u∣−4a0forforotherwise∣xi−u∣≤11<∣xi−u∣<2
同理,也可以得到 w y j w_{yj} wyj,则可以得到权重的表达式如下公式所示:
w i j = w x i × w y j w_{ij}=w_{xi}\times w_{yj} wij=wxi×wyj
c++代码实现:
"bmp.h"的头文件代码:bmp图片的类以及读写函数 bmp.h
#include
#include
#include
#include
#include
#include
#include "bmp.h"
using namespace std;
#define DRAW_HEIGHT 512 //目标图像高度
#define DRAW_WIDTH 512 //目标图像宽度
double a=-0.5; //BiCubic基函数
void getW_x(double w_x[4],double x);
void getW_y(double w_y[4], double y);
int main()
{
BMP rbmp;
BMP wbmp(DRAW_WIDTH,DRAW_HEIGHT);
char strFile[50] = "./lena24.bmp";//打开图像路径,BMP图像必须为24位真彩色格式
char strFilesave[50] = "./test.bmp";//处理后图像存储路径
//读取位图的数据
imread(strFile,rbmp);
int width = rbmp.cols();
int height = rbmp.rows();
int l_width = WIDTHBYTES(width*24);//计算位图的实际宽度并确保它为4byte的倍数
//写位图的数据
int write_width = WIDTHBYTES(DRAW_WIDTH*24);//计算写位图的实际宽度(字节)并确保它为4byte的倍数
/*******************图像处理部分******************/
for (int hnum = 2; hnum < DRAW_HEIGHT-4; hnum++)
{
for (int wnum = 2; wnum < DRAW_WIDTH-4; wnum++)
{
double d_original_img_hnum = hnum*height / (double)DRAW_HEIGHT;
double d_original_img_wnum = wnum*width / (double)DRAW_WIDTH;
int i_original_img_hnum = d_original_img_hnum;//距离最近的点坐标
int i_original_img_wnum = d_original_img_wnum;//距离最近的点坐标
double w_x[4], w_y[4];//行列方向的加权系数
getW_x(w_x, d_original_img_hnum);
getW_y(w_y, d_original_img_wnum);
int pixel_point = hnum*write_width + wnum * 3;//映射尺度变换图像数组位置偏移量
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
int original_point=(i_original_img_hnum+i-1)*l_width + (i_original_img_wnum+j-1)*3;
wbmp.pColorData[pixel_point]+=rbmp.pColorData[original_point]*w_x[i]*w_y[j];
wbmp.pColorData[pixel_point + 1]+=rbmp.pColorData[original_point+1]*w_x[i]*w_y[j];
wbmp.pColorData[pixel_point + 2]+=rbmp.pColorData[original_point+2]*w_x[i]*w_y[j];
}
}
}
}
/*******************图像处理部分******************/
std::cout<<"Done!"<<std::endl;
imwrite(strFilesave,wbmp);
system("pause");
return 0;
}
/*计算系数*/
void getW_x(double w_x[4],double x){
int X = (int)x;//取整数部分
double stemp_x[4];
stemp_x[0] = 1 + (x - X);
stemp_x[1] = x - X;
stemp_x[2] = 1 - (x - X);
stemp_x[3] = 2 - (x - X);
w_x[0] = a*abs(stemp_x[0] * stemp_x[0] * stemp_x[0]) - 5 * a*stemp_x[0] * stemp_x[0] + 8 * a*abs(stemp_x[0]) - 4 * a;
w_x[1] = (a + 2)*abs(stemp_x[1] * stemp_x[1] * stemp_x[1]) - (a + 3)*stemp_x[1] * stemp_x[1] + 1;
w_x[2] = (a + 2)*abs(stemp_x[2] * stemp_x[2] * stemp_x[2]) - (a + 3)*stemp_x[2] * stemp_x[2] + 1;
w_x[3] = a*abs(stemp_x[3] * stemp_x[3] * stemp_x[3]) - 5 * a*stemp_x[3] * stemp_x[3] + 8 * a*abs(stemp_x[3]) - 4 * a;
}
void getW_y(double w_y[4], double y){
int Y = (int)y;
double stemp_y[4];
stemp_y[0] = 1.0 + (y - Y);
stemp_y[1] = y - Y;
stemp_y[2] = 1 - (y - Y);
stemp_y[3] = 2 - (y - Y);
w_y[0] = a*abs(stemp_y[0] * stemp_y[0] * stemp_y[0]) - 5 * a*stemp_y[0] * stemp_y[0] + 8 * a*abs(stemp_y[0]) - 4 * a;
w_y[1] = (a + 2)*abs(stemp_y[1] * stemp_y[1] * stemp_y[1]) - (a + 3)*stemp_y[1] * stemp_y[1] + 1;
w_y[2] = (a + 2)*abs(stemp_y[2] * stemp_y[2] * stemp_y[2]) - (a + 3)*stemp_y[2] * stemp_y[2] + 1;
w_y[3] = a*abs(stemp_y[3] * stemp_y[3] * stemp_y[3]) - 5 * a*stemp_y[3] * stemp_y[3] + 8 * a*abs(stemp_y[3]) - 4 * a;
}