插值之双线性插值

版权声明:本文为博主原创文章,未经博主允许不得转载。 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];
    }
}


猜你喜欢

转载自blog.csdn.net/Jacky_Ponder/article/details/65630036