1、概念介绍

1.1  最近邻插值:

最近邻插值算法根据原图像与目标图像的尺寸,计算缩放比例,然后根据缩放比例,计算目标图像所对应的原像素。过程中会产生小数,然后四舍五入,取与这个点最近的点。

example:

100

110

120

110

120

130

120

130

140

                                                                              表1 src原图像 3x3 (代表img.rows=3、img.cols=3)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

                                                                              表2 dst目标图像 6x6 (代表img.rows=6、img.cols=6)

step1:计算原图像与目标图像的比例

scale_h = src.rows/dst.rows = 1/2
scale_w = src.cols/dst.cols = 1/2

step2:根据宽高比例计算原图相对应的x、y坐标

i_scale = i*scale_h
j_scale = j*scale_w

例:目标图像的坐标

(0,0)(0,1)(0,2)(0,3)(0,4)(0,5)
(1,0)(1,1)(1,2)(1,3)(1,4)(1,5)
(2,0)(2,1)(2,2)(2,3)(2,4)(2,5)
(3,0)(3,1)(3,2)(3,3)(3,4)(3,5)
(4,0)(4,1)(4,2)(4,3)(4,4)(4,5)
(5,0)(5,1)(5,2)(5,3)(5,4)(5,5)

代入得:

(0,0)点:

i_scale = 0*(1/2) = 0
j_scale = 0*(1/2) = 0

则取原图像中(0,0)点的像素值100给目标图像的(0,0)

(0,1)点:

i_scale = 0*(1/2) = 0
j_scale = 1*(1/2) = 1/2≈0

则取原图像中的(0,0)点的像素值100给目标图像(0,1)点

(0,2)点:

i_scale = 0*(1/2) = 0
j_scale = 2*(1/2) = 1

则取原图像中的(0,1)点的像素值110给目标图像(0,2)点

。。。

依此类推得出目标图像的像素值。

// 最近邻插值算法
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
void scale(cv::Mat& input_img, int width, int height);

int main()
{
	// 最近邻内插
	cv::Mat img = cv::imread("C:/Users/lenovo/Pictures/Saved Pictures/Lena.png",0);
	cv::imshow("src", img);
	scale(img, 450, 300);

	return 0;

}

// 获取原图像相应坐标的像素值
uchar get_scale_value(cv::Mat input_img, int i, int j)
{
	uchar* p = input_img.ptr<uchar>(i);
	return p[j];
}

void scale(cv::Mat& input_img, int width, int height)
{
	cv::Mat output_img(height, width, CV_8UC1);
	output_img.setTo(0);
	float h_scale_rate = (float)input_img.rows / height;	// 高的比例
	float w_scale_rate = (float)input_img.cols / width;		// 宽的比例
	for (int i = 0; i < height; i++)
	{
		uchar* p = output_img.ptr<uchar>(i);
		for (int j = 0; j < width; j++)
		{
			int i_scale = h_scale_rate * i;		// 依照高的比例计算原图相应坐标中的x,这里采用的是向下取整,当然四舍五入也可以
			int j_scale = w_scale_rate * j;		// 依照宽的比例计算原图相应坐标中的y

			p[j] = get_scale_value(input_img, i_scale, j_scale);
		}
	}
	cv::imshow("scale", output_img);
	cv::imwrite("result.png", output_img);
	cv::waitKey();

}

OpenCV resize 双线性插值 opencv插值方法_插值

                                                   图1 最近邻插值效果图

1.2双线性插值

OpenCV resize 双线性插值 opencv插值方法_#include_02

f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)

对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为(i+u,j+v) (其中i、j均为浮点坐标的整数部分,u、v为浮点坐标的小数部分,是取值[0,1)区间的浮点数),则这个像素得值 f(i+u,j+v) 可由原图像中坐标为 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的周围四个像素的值决定,即:f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)
其中f(i,j)表示源图像(i,j)处的的像素值,以此类推。

比如,象刚才的例子,现在假如目标图的象素坐标为(1,1),那么反推得到的对应于源图的坐标是(0.75 , 0.75), 这其实只是一个概念上的虚拟象素,实际在源图中并不存在这样一个象素,那么目标图的象素(1,1)的取值不能够由这个虚拟象素来决定,而只能由源图的这四个象素共同决定:(0,0)(0,1)(1,0)(1,1),而由于(0.75,0.75)离(1,1)要更近一些,那么(1,1)所起的决定作用更大一些,这从公式1中的系数uv=0.75×0.75就可以体现出来,而(0.75,0.75)离(0,0)最远,所以(0,0)所起的决定作用就要小一些,公式中系数为(1-u)(1-v)=0.25×0.25也体现出了这一特点。

双线性内值算法的核心思想就是上面的那个公式,它解释了对于原图不存在的浮点像素(比如<1.5,1.5>)是如何确定其实际值的。其实它是以4个相邻的像素值来共同确定,即<1,1> <2,1> <1,2> <2,2>。谁离<1,1>比较近,谁就对它起的影响比较大,这些都在公式中有所体现,这就是双线性插值的精髓。

// 双线性插值
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
void bin_linear_scale(cv::Mat& input_img, int width, int height);

int main()
{
	cv::Mat img = cv::imread("C:/Users/lenovo/Pictures/Saved Pictures/Lena.png", 0);
	cv::imshow("src", img);
	bin_linear_scale(img, 200, 100);

	return 0;
}

// 双线性插值公式
// f(i+u, j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1)+u(1-v)f(i+1,j)+uvf(i+1,j+1)
uchar get_scale_value(cv::Mat& input_img, float raw_i, float raw_j)
{
	int i = raw_i;
	int j = raw_j;
	float u = raw_i - i;
	float v = raw_j - j;

	// 注意处理边界问题,容易越界
	if (i + 1 >= input_img.rows || j + 1 >= input_img.cols)
	{
		uchar* p = input_img.ptr<uchar>(i);
		return p[j];
	}
	uchar* p = input_img.ptr<uchar>(i);
	uchar x1 = p[j];		// f(i,j)
	uchar x2 = p[j + 1];	// f(i,j+1)
	p = input_img.ptr<uchar>(i + 1);
	uchar x3 = p[j];		// f(i+1,j)
	uchar x4 = p[j + 1];	// f(i+1,j+1)
	
	return ((1 - u)*(1 - v)*x1 + (1 - u)*v*x2 + u*(1 - v)*x3 + u*v*x4);
}

void bin_linear_scale(cv::Mat& input_img, int width, int height)
{
	cv::Mat output_img(height, width, CV_8UC1);
	output_img.setTo(0);
	float h_scale_rate = (float)input_img.rows / height;
	float w_scale_rate = (float)input_img.cols / width;
	for (int i = 0; i < height; i++)
	{
		uchar* p = output_img.ptr<uchar>(i);
		for (int j = 0; j < width; j++)
		{
			float i_scale = h_scale_rate*i;
			float j_scale = w_scale_rate*j;

			p[j] = get_scale_value(input_img, i_scale, j_scale);
		}
	}
	cv::imshow("scale", output_img);
	cv::imwrite("result.png", output_img);
	cv::waitKey();

}

OpenCV resize 双线性插值 opencv插值方法_OpenCV resize 双线性插值_03

                                                     图2 双线性插值效果图