版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Jacky_Ponder/article/details/65630036
1.1双线性插值简介
双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。在双线性插值中,新创造的象素值,是由原图像位置在它附近的(2 x -2)4个邻近象素的值通过加权平均计算得出的。这种平均算法具有放锯齿效果,创造出来的图像拥有平滑的边缘,锯齿难以察觉。
如下图,已知Q12,Q22,Q11,Q21,但是要插值的点为P点,这就要用双线性插值了,首先在x轴方向上,对R1和R2两个点进行插值,这个很简单,然后根据R1和R2对P点进行插值,这就是所谓的双线性插值。
假如我们想得到未知函数在点的值,假设我们已知函数在, , ,及 四个点的值。
首先在 x 方向进行线性插值,得到
然后在 y 方向进行线性插值,得到
这样就得到所要的结果 ,
如果选择一个坐标系统使得的四个已知点坐标分别为 (0, 0)、(0, 1)、(1, 0) 和 (1, 1),那么插值公式就可以化简为
或者用矩阵运算表示为
这种插值方法的结果通常不是线性的,线性插值的结果与插值的顺序无关。首先进行 y 方向的插值,然后进行 x 方向的插值,所得到的结果是一样的。
1.2范例
#include <cv.h>
#include <highgui.h>
#include <iostream>
using namespace std;
float Abs(float f);
void zoom(IplImage* src, IplImage* dst);
int main() {
// read an image
IplImage* src = cvLoadImage("E://pic//view.jpg");
IplImage* dst = cvCreateImage(cvSize(1000,1000), src->depth, src->nChannels);
zoom(src, dst);
cvShowImage("src", src);
cvShowImage("dst", dst);
cvWaitKey(0);
return 1;
}
/****************************
*返回绝对值
*****************************/
float Abs(float f)
{
return f>=0 ? f : -f;
}
/****************************
*目标: 实现图像的缩放,
使用了双线性插值的算法
*参数: src源图像
dst生成图像
*返回值:无
*****************************/
void zoom(IplImage* src, IplImage* dst)
{
int srcWidth = src->width;
int srcHeight = src->height;
int dstWidth = dst->width;
int dstHeight = dst->height;
//源图像与目标图像的宽高比例,这里减1很重要,否则有时报错,有时不报错。这点困扰了我很久
const float tx = (srcWidth-1.0f)/(dstWidth-1.0f);
const float ty = (srcHeight-1.0f)/(dstHeight-1.0f);
CvPoint2D32f uv;//存储源图像的浮点坐标
CvPoint3D32f f1;
CvPoint3D32f f2;
for (int j=0; j<dstHeight-1; j++)
{
for (int i=0; i<dstWidth-1; i++)
{
uv.x = i*tx;
uv.y = j*ty;
int iu = (int)uv.x;
int iv = (int)uv.y;
f1.x = ((uchar*)(src->imageData + src->widthStep*iv))[iu*3+0] * (1-Abs(uv.x-iu))+
((uchar*)(src->imageData + src->widthStep*iv))[(iu+1)*3+0] * (uv.x-iu);
f1.y = ((uchar*)(src->imageData + src->widthStep*iv))[iu*3+1] * (1-Abs(uv.x-iu))+
((uchar*)(src->imageData + src->widthStep*iv))[(iu+1)*3+1] * (uv.x-iu);
f1.z = ((uchar*)(src->imageData + src->widthStep*iv))[iu*3+2] * (1-Abs(uv.x-iu))+
((uchar*)(src->imageData + src->widthStep*iv))[(iu+1)*3+2] * (uv.x-iu);
f2.x = ((uchar*)(src->imageData + src->widthStep*(iv+1)))[iu*3] * (1-Abs(uv.x-iu))
+((uchar*)(src->imageData + src->widthStep*(iv+1)))[(iu+1)*3] * (uv.x-iu);
f2.y = ((uchar*)(src->imageData + src->widthStep*(iv+1)))[iu*3+1] * (1-Abs(uv.x-iu))+
((uchar*)(src->imageData + src->widthStep*(iv+1)))[(iu+1)*3+1] * (uv.x-iu);
f2.z = ((uchar*)(src->imageData + src->widthStep*(iv+1)))[iu*3+2] * (1-Abs(uv.x-iu))+
((uchar*)(src->imageData + src->widthStep*(iv+1)))[(iu+1)*3+2] * (uv.x-iu);
((uchar*)(dst->imageData + dst->widthStep*j))[i*3] = f1.x*(1-Abs(uv.y-iv))+f2.x*(Abs(uv.y-iv));
((uchar*)(dst->imageData + dst->widthStep*j))[i*3+1] = f1.y*(1-Abs(uv.y-iv))+f2.y*(Abs(uv.y-iv));
((uchar*)(dst->imageData + dst->widthStep*j))[i*3+2] = f1.z*(1-Abs(uv.y-iv))+f2.z*(Abs(uv.y-iv));
}
//这里添加上最后一列
((uchar*)(dst->imageData + dst->widthStep*j))[(dstWidth-1)*3] = ((uchar*)(dst->imageData + dst->widthStep*j))[(dstWidth-2)*3];
((uchar*)(dst->imageData + dst->widthStep*j))[(dstWidth-1)*3+1] = ((uchar*)(dst->imageData + dst->widthStep*j))[(dstWidth-2)*3+1];
((uchar*)(dst->imageData + dst->widthStep*j))[(dstWidth-1)*3+2] = ((uchar*)(dst->imageData + dst->widthStep*j))[(dstWidth-2)*3+2];
}
//这里添加上最后一行
for(int i=0; i<dstWidth*3; i++)
{
((uchar*)(dst->imageData + dst->widthStep*(dstHeight-1)))[i] = ((uchar*)(dst->imageData + dst->widthStep*(dstHeight-2)))[i];
}
}