双线性插值
双线性插值作为一种滤波器,广泛应用在信号处理,数字图像和视频处理等应用中。
双线性插值 Bilinear Interpolation
介绍
双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。
示例
已知的红色数据点与待插值得到的绿色点
假如我们想得到未知函数 f在点 P= ( x, y) 的值,假设我们已知函数 f在 Q11 = ( x1, y1)、 Q12 = ( x1, y2), Q21 = ( x2, y1) 以及 Q22 = ( x2, y2) 四个点的值。
首先在 x方向进行线性插值,得到R1和R2,然后在 y方向进行线性插值,得到P.
这样就得到所要的结果 f( x, y).
其中红色点Q11,Q12,Q21,Q22为已知的4个像素点.
点R1和R2插值 ,由R1与R2计算P点.
线性插值的结果与插值的顺序无关。首先进行
y方向的插值,然后进行
x方向的插值,所得到的结果是一样的。但双线性插值插值方法这种方法并不是线性的,首先进行
y方向的插值,然后进行
x方向的插值,与首先进行 x方向的插值,然后进行 y方向的插值,所得到的R1与R2是不一样的。
如果选择一个坐标系统使得 的四个已知点坐标分别为 (0, 0)、(0, 1)、(1, 0) 和 (1, 1),那么插值公式就可以化简为
f(x,y)=f(0,0)(1-x)(1-y)+f(0,1)(1-x)y+f(1,1)xy+f(1,0)x(1-y)
- 在x与y方向上,z值成单调性特性的应用中,此种方法可以做外插运算,即可以求解Q1~Q4所构成的正方形以外的点的值。
- 双线性插值的一个显然的 三维空间延伸是三线性插值。
- 三线性插值的方法可参看matlab中的interp3
特点编辑
当对相邻四个像素点采用双线性插值时,所得表面在邻域处是吻合的,但斜率不吻合。并且双线性灰度插值的平滑作用可能使得图像的细节产生退化,这种现象在进行图像放大时尤其明显。
代码如下:
IplImage *img = cvLoadImage("C:\\Users\\Administrator\\Desktop\\3838.jpg");
int nw = img->width;
int nh = img->height;
void Ctry::OnTryTyr1()
{
//TODO: 在此添加命令处理程序代码
double times = 0.8; //比例因子
int nWidth =int( times * (img->width));
int nHeight =int( times * (img->height));
IplImage *dst = cvCreateImage(cvSize(nWidth, nHeight), 8, 3);
CvScalar pixel1;
for (int i = 0; i < dst->width; i++)
{
for (int j = 0; j < dst->height; j++)
{
if (int(i * 1 / times) < img->width && int(j * 1 / times) < img->height)
{
pixel1 = InterpBilinear((i * 1 / times), (j * 1 / times ));
cvSet2D(dst, j, i, pixel1);
}
else
{
cvSet2D(dst, j, i, RGB(0, 0, 0, ));
}
}
}
cvSaveImage("C:\\Users\\Administrator\\Desktop\\InterpBilinear.jpg", dst);
cvNamedWindow(" dst", CV_WINDOW_AUTOSIZE);
cvShowImage(" dst", dst);
cvNamedWindow("img", CV_WINDOW_AUTOSIZE);
cvShowImage("img", img);
cvWaitKey(0);
cvReleaseImage(&img);
cvReleaseImage(&dst);
cvDestroyWindow(" dst");
cvDestroyWindow("img");
}
CvScalar Ctry::InterpBilinear(double x, double y)
{
//4个邻近点的坐标(i1,j1),(i2,j1),(i1,j2),(i2,j2)
int x1, x2;
int y1, y2;
//4个最邻近像素值
CvScalar f1, f2, f3, f4;
//二个插值中间值
CvScalar f12, f34;
CvScalar pixel;
double epsilon = 0.0001;
//计算四个最邻近像素的坐标
x1 = int(x);
x2 = x1 + 1;
y1 = int(y);
y2 = y1 + 1;
if ((x<0) || (x>nw - 1) || (y<0) || (y>nh - 1))
{
//要计算的点不在原图范围内
MessageBox(NULL, "超过图片大小", "错误", MB_OK | MB_ICONERROR);
return 0;
}
else
{
if (fabs(x - nw + 1 )<= epsilon)
{
//要计算的点在图像的右边缘上
if (fabs(y - nh + 1) <= epsilon)
{
//要计算的元素恰好是图像最右下角的那一个元素,直接返回该点的像素值
f1 = cvGet2D(img, y1, x1);
return f1;
}
else
{
//在图像右边缘且不是最后一点,直接一次插值即可
f1 = cvGet2D(img, y1, x1);
f3 = cvGet2D(img, y1, x2);
pixel.val[0] = f1.val[0] + (y - y1)*(f3.val[0] - f1.val[0]);
pixel.val[1] = f1.val[1] + (y - y1)*(f3.val[1] - f1.val[1]);
pixel.val[2] = f1.val[2] + (y - y1)*(f3.val[2] - f1.val[2]);
return pixel;
}
}
else if (fabs(y - nh + 1) <= epsilon)
{
//要计算的点在图像的下边缘上且不是最后一点,直接一次插值即可
f1 = cvGet2D(img, y1, x1);
f2 = cvGet2D(img, y2, x1);
pixel.val[0] = f1.val[0] + (x - x1)*(f2.val[0] - f1.val[0]);
pixel.val[1] = f1.val[1] + (x - x1)*(f2.val[1] - f1.val[1]);
pixel.val[2] = f1.val[2] + (x - x1)*(f2.val[2] - f1.val[2]);
return pixel;
}
else
{
//计算4个最邻近像素值
f1 = cvGet2D(img, y1, x1);
f2 = cvGet2D(img, y2, x1);
f3 = cvGet2D(img, y1, x2);
f4 = cvGet2D(img, y2, x2);
//插值1
f12.val[0] = f1.val[0] + (x - x1)*(f2.val[0] - f1.val[0]);
f12.val[1] = f1.val[1] + (x - x1)*(f2.val[1] - f1.val[1]);
f12.val[2] = f1.val[2] + (x - x1)*(f2.val[2] - f1.val[2]);
//插值2
f34.val[0] = f3.val[0] + (x - x1)*(f4.val[0] - f3.val[0]);
f34.val[1] = f3.val[1] + (x - x1)*(f4.val[1] - f3.val[1]);
f34.val[2] = f3.val[2] + (x - x1)*(f4.val[2] - f3.val[2]);
//插值3
pixel.val[0] = f12.val[0] + (y - y1)*(f34.val[0] - f12.val[0]);
pixel.val[1] = f12.val[1] + (y - y1)*(f34.val[1] - f12.val[1]);
pixel.val[2] = f12.val[2] + (y - y1)*(f34.val[2] - f12.val[2]);
return pixel;
}
}
}
效果图: