假设原图图片的宽度为yw
,高度为xh
变换图的宽度为jw
,高度为ih
于是对于变换图中任意一个像素点(j’, i’)我们可以用以下的方法映射到原图中去:
- 1
- 2
- 3
通常情况下,y’和x’不为整数。
例如,原图尺寸:
- 1
- 2
- 3
变换图尺寸:
- 1
- 2
- 3
对于变换图中的(400, 400)像素点:
- 1
- 2
- 3
我们将变换图中的(400, 400)像素点映射到原图中的(571.42857, 457.14286)。
于是我们就用原图中对应的4点(571,457),(572,457),(571,458),(572,458)来确定变换图像中的(400,400)点像素值。
- 1
- 2
- 3
我们认为距离(y’, x’)点距离越近,其对目标像素影响的权重应该越大,距离越远,影响权重越小。
(571,457)点权重为s4面积,(572,457)点权重为s3面积,(571,458)点权重为s2面积,(572,458)点权重为s1面积。
于是我们得到:
(400,400)目标图 = (571,457)xs4 + (572,457)xs3 + (571,458)xs2 + (572,458)xs1
其中,s1+s2+s3+s4 = 1
以上就是双线性插值法目标像素计算公式。
第二种理解原理的方法:
其中的u,v表示的是小数的位置
对于 (i, j+v),f(i, j) 到 f(i, j+1) 的灰度变化为线性关系,则有:
f(i, j+v) = [f(i, j+1) - f(i, j)] * v + f(i, j)
同理对于 (i+1, j+v) 则有:
f(i+1, j+v) = [f(i+1, j+1) - f(i+1, j)] * v + f(i+1, j)
从f(i, j+v) 到 f(i+1, j+v) 的灰度变化也为线性关系,由此可推导出待求象素灰度的计算式如下:
f(i+u, j+v) = (1-u) * (1-v) * f(i, j) + (1-u) * v * f(i, j+1) + u * (1-v) * f(i+1, j) + u * v * f(i+1, j+1)
双线性内插法的计算比最邻近点法复杂,计算量较大,但没有灰度不连续的缺点,结果基本令人满意。它具有低通滤波性质,使高频分量受损,图像轮廓可能会有一点模糊。
下面附上源代码:
#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
#include<iostream>
using namespace cv;
//双线性插值算法
//f(i+u, j+v) = (1-u) * (1-v) * f(i, j) + (1-u) * v * f(i, j+1) + u * (1-v) * f(i+1, j) + u * v * f(i+1, j+1)
double xm = 0;//映射在原图中的x
double ym = 0;//映射在原图中的y
int xi = 0;//映射x的整数部分
int yi = 0;//映射y的整数部分
int x11 = 0;//xi+1
int y11 = 0;//yi+1
double xs = 0;//xm-xi
double ys = 0;//ym-yi
Mat BilinearInter(Mat &srcImage, double kx, double ky)
{
//获取输出图像的分辨率
int nRows = cvRound(srcImage.rows*kx);
int nCols = cvRound(srcImage.cols*ky);
Mat resultImage(nRows, nCols, srcImage.type());
for (int i = 0; i < nRows; i++)
{
for (int j = 0; j < nCols; j++)
{
//获取目标图像(i,j)在原图中的坐标
xm = i / kx;
ym = j / ky;
//取出映射到原图的坐标的整数部分
xi = (int)xm;
yi = (int)ym;
//取出偏移量
xs = xm - xi;//小数部分
ys = ym - yi;
x11 = xi + 1;//整数部分加1
y11 = yi + 1;
//边缘点
if (x11 > (srcImage.rows-1))
x11 = xi - 1;
if (y11 > (srcImage.cols-1))
y11 = yi - 1;
//b
resultImage.at<Vec3b>(i, j)[0] = (int)(srcImage.at<Vec3b>(xi, yi)[0] * (1 - xs)*(1 - ys) +
srcImage.at<Vec3b>(xi, y11)[0] * (1 - xs) * ys +
srcImage.at<Vec3b>(x11, yi)[0] * xs*(1 - ys) +
srcImage.at<Vec3b>(x11, y11)[0] * xs * ys);
//g
resultImage.at<Vec3b>(i, j)[1] = (int)(srcImage.at<Vec3b>(xi, yi)[1] * (1 - xs)*(1 - ys) +
srcImage.at<Vec3b>(xi, y11)[1] * (1 - xs) * ys +
srcImage.at<Vec3b>(x11, yi)[1] * xs*(1 - ys) +
srcImage.at<Vec3b>(x11, y11)[1] * xs * ys);
//r
resultImage.at<Vec3b>(i, j)[2] = (int)(srcImage.at<Vec3b>(xi, yi)[2] * (1 - xs)*(1 - ys) +
srcImage.at<Vec3b>(xi, y11)[2] * (1 - xs) * ys +
srcImage.at<Vec3b>(x11, yi)[2] * xs*(1 - ys) +
srcImage.at<Vec3b>(x11, y11)[2] * xs * ys);
/*
resultImage.at<Vec3b>(i, j) = (srcImage.at<Vec3b>(xi, yi) * (1 - xs)*(1 - ys) +
srcImage.at<Vec3b>(xi, y11) * (1 - xs) * ys +
srcImage.at<Vec3b>(x11, yi) * xs*(1 - ys) +
srcImage.at<Vec3b>(x11, y11) * xs * ys);
*/
}
}
return resultImage;
}
int main()
{
Mat srcImage = imread("lakeWater.jpg");
if (!srcImage.data)
{
printf("image could not load...\n");
return -1;
}
imshow("srcImage", srcImage);
Mat resultImage = BilinearInter(srcImage, 1.5, 2);
imshow("resultImage", resultImage);
waitKey(0);
return 0;
}
原图:
效果图: