双线性插值概念
双线性插值,顾名思义就是两个方向的线性插值叠加。所以只要了解什么是线性插值,分别在x轴和y轴都做一遍,就是双线性插值了。
线性插值的概念也非常简单粗暴,就是两个点A,B,要在AB中间插入一个点C(点C坐标在AB连线上),就直接让C的值落在AB的值的连线上就可以了。
如A点坐标(0,0),值为3,B点坐标(0,2),值为5,那要对坐标为(0,1)的点C进行插值,就让C落在AB线上,值为4就可以了。
但是如果C不在AB的线上肿么办捏,所以就有了双线性插值。如图,已知Q12,Q22,Q11,Q21,但是要插值的点为P点,这就要用双线性插值了,首先在x轴方向上,对R1和R2两个点进行插值,这个很简单,然后根据R1和R2对P点进行插值,这就是所谓的双线性插值。
附:维基百科--双线性插值:
双线性插值原理剖析
双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值。
假如我们想得到未知函数
在点
的值,假设我们已知函数
在
,
,
, 及
四个点的值。
首先在 x 方向进行线性插值,得到
然后在 y 方向进行线性插值,得到
这样就得到所要的结果
,
如果选择一个坐标系统使得
的四个已知点坐标分别为 (0, 0)、(0, 1)、(1, 0) 和 (1, 1),那么插值公式就可以化简为
或者用矩阵运算表示为
与这种插值方法名称不同的是,这种插值方法的结果通常不是线性的,它的形式是
常数的数目都对应于给定的 f 的数据点数目
线性插值的结果与插值的顺序无关。首先进行 y 方向的插值,然后进行 x 方向的插值,所得到的结果是一样的。
双线性插值实现
void ImgResize_BiLinear()
{
cv::Mat imgSrc, imgDst1, imgDst2;
imgSrc = cv::imread("test.jpg");
imgDst1 = cv::Mat(cv::Size(imgSrc.cols*2, imgSrc.rows*2), imgSrc.type(), cv::Scalar::all(0));
imgDst2 = cv::Mat(imgDst1.size(), imgSrc.type(), cv::Scalar::all(0));
double scale_x = (double)imgSrc.cols / imgDst1.cols;
double scale_y = (double)imgSrc.rows / imgDst1.rows;
uchar* dataDst = imgDst1.data;
int stepDst = imgDst1.step;
uchar* dataSrc = imgSrc.data;
int stepSrc = imgSrc.step;
int iWidthSrc = imgSrc.cols;
int iHiehgtSrc = imgSrc.rows;
short cbufy[2];
float y_float, x_float;
int y_int, x_int;
short cbufx[2];
for (int j=0; j<imgDst1.rows; ++j)
{
y_float = (float)((j + 0.5) * scale_y - 0.5);
y_int = cvFloor(y_float);
y_float -= y_int;
y_int = std::min(y_int, iHiehgtSrc - 2);
y_int = std::max(0, y_int);
cbufy[0] = cv::saturate_cast<short>((1.f - y_float) * 2048);
cbufy[1] = 2048 - cbufy[0];
for (int i=0; i<imgDst1.cols; ++i)
{
x_float = (float)((i + 0.5) * scale_x - 0.5);
x_int = cvFloor(x_float);
x_float -= x_int;
if (x_int < 0)
{
x_float = 0, x_int = 0;
}
if (x_int >= iWidthSrc - 1)
{
x_float = 0, x_int = iWidthSrc - 2;
}
cbufx[0] = cv::saturate_cast<short>((1.f - x_float) * 2048);
cbufx[1] = 2048 - cbufx[0];
for (int k = 0; k < imgSrc.channels(); ++k)
{
*(dataDst+ j*stepDst + 3*i + k) = (*(dataSrc + y_int*stepSrc + 3*x_int + k) * cbufx[0] * cbufy[0] +
*(dataSrc + (y_int+1)*stepSrc + 3*x_int + k) * cbufx[0] * cbufy[1] +
*(dataSrc + y_int*stepSrc + 3*(x_int+1) + k) * cbufx[1] * cbufy[0] +
*(dataSrc + (y_int+1)*stepSrc + 3*(x_int+1) + k) * cbufx[1] * cbufy[1]) >> 22;
}
}
}
cv::imwrite("result_1.jpg", imgDst1);
cv::resize(imgSrc, imgDst2, imgDst1.size(), 0, 0, 1);
cv::imwrite("result_2.jpg", imgDst2);
}
上述代码为3通道彩图的缩放代码,图片的加载保存用了Opencv的结构体,其余基本保持C的风格;