图像插值算法之双线性插值

假设原图图片的宽度为yw,高度为xh

变换图的宽度为jw,高度为ih

于是对于变换图中任意一个像素点(j’, i’)我们可以用以下的方法映射到原图中去:

y' = yw/jw * j'
x' = xh/ih * i'

 
 
  • 1
  • 2
  • 3

通常情况下,y’和x’不为整数。

例如,原图尺寸:

yw = 1000
xh = 800

 
 
  • 1
  • 2
  • 3

变换图尺寸:

jw = 700
ih = 700

 
 
  • 1
  • 2
  • 3

对于变换图中的(400, 400)像素点:

y' = 1000/700 * 400 = 571.42857
x' = 800/700 * 400 = 457.14286

 
 
  • 1
  • 2
  • 3

我们将变换图中的(400, 400)像素点映射到原图中的(571.42857, 457.14286)。

于是我们就用原图中对应的4点(571,457),(572,457),(571,458),(572,458)来确定变换图像中的(400,400)点像素值。

t = 571.42857 - 571 = 0.42857
u = 457.14286 - 457 = 0.14286

 
 
  • 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;
}


原图:


效果图:


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值