图像插值算法的原理及C++实现

简介:

在图像的处理过程中,经常需要对图像进行尺寸变换、旋转或者扭曲等操作,在进行这些操作之后,原图像的尺寸往往就发生了改变,为了保持变换后的图像不失真,就需要对图像进行插值。

常见的插值方法有最近邻插值和双线性插值。

最近邻插值:

最近邻插值是最简单的一种插值方式,就是在对图像进行坐标变换之后,选取原图像中距离最近的像素点进行插值(简单说就是将目标图像的像素位置映射到源图像之后,对x、y轴的值分别进行四舍五入)。如下式所示:

res.x = dest.x / dest.width * res.width
res.y = dest.y / dest.height * res.height

其中 <em>res</em> 表示原图像,dest 表示目标图像。其中计算后得到的 res.x 以及 res.y 可能不是整型,但是对于一幅图像来说,像素的坐标一定是整数,所以就要对这两个值进行四舍五入,方法很简单,如下所示:

res_x = (int)((float)dest_x / width * res.rows + 0.5);
res_y = (int)((float)dest_y / width * res.cols + 0.5);

双线性插值:

最近邻插值虽然原理简单,也易于实现,但是对于较精细的图像,容易丢失有用的像素点信息,而且当图像尺寸很小时,插值结果会很不准确,相比之下,双线性插值能够得到更好的效果。双线性插值是利用周围的四个像素点的像素信息,来估计目标点的像素值。

对于一个目的像素,设置坐标通过反向变换得到的浮点坐标为 (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) 

代码:

下面,基于 opencv 中的 Mat 类,分别给出了最近邻插值和双线性插值的代码实现:

inter_define.h
 

#pragma once
/****************************************************************
* project: Interpolation Algrithom
* author:  xhj
* email:   [email protected]
* date:    2018/ 9/11
*****************************************************************/
#include <iostream>
#include "opencv2/opencv.hpp"
#define NEAREST 0
#define BILINEAR 1

using namespace std;
using namespace cv;

bool resize_img(const Mat &res, Mat &dest, int height, int width, int type = NEAREST);

inter_define.cpp

#include "stdafx.h"
#include "interp_define.h"

bool resize_img(const Mat &res, Mat &dest, int height, int width, int type)
{
	/*
	res.x = dest.x / dest.width * res.width
	res.y = dest.y / dest.height * res.height
	*/
	if (res.empty())
	{
		cout << "The resource image is empty!" << endl;
		return false;
	}

	Mat _tmp(height, width, CV_8U);
	int res_x = 0, res_y = 0;
	double _x = 0.0, _y = 0.0, _u = 0.0, _v = 0.0;

	if (NEAREST == type)
	{
		for (int i = 0; i < height; i++)
		{
			for (int j = 0; j < width; j++)
			{
				res_x = (int)((float)i / width * res.rows + 0.5);
				res_y = (int)((float)j / width * res.cols + 0.5);
				//cout << res_x << res_y << endl;
				_tmp.at<uchar>(i, j) = res.at<uchar>(res_x, res_y);
			}
		}
	}

	else if (BILINEAR == type)
	{
		for (int i = 0; i < height; i++)
		{
			for (int j = 0; j < width; j++)
			{
				_x = (double)i / width *res.rows;
				_y = (double)j / width*res.cols;
				res_x = (int)_x;
				res_y = (int)_y;
				_u = _x - res_x;
				_v = _y - res_y;
				//cout << _u << _v << endl;
				_tmp.at<uchar>(i, j) = (1 - _u) * (1 - _v) * res.at<uchar>(res_x, res_y) + \
					(1 - _u) * _v * res.at<uchar>(res_x, res_y + 1) + \
					_u * (1 - _v) * res.at<uchar>(res_x + 1, res_y) + \
					_u * _v * res.at<uchar>(res_x + 1, res_y + 1);
			}
		}
	}

	else
	{
		cout << "Don't have the type: " << type << endl;
		return false;
	}

	dest = _tmp.clone();
	return true;
}

猜你喜欢

转载自blog.csdn.net/xhj_enen/article/details/82623258