简介:
在图像的处理过程中,经常需要对图像进行尺寸变换、旋转或者扭曲等操作,在进行这些操作之后,原图像的尺寸往往就发生了改变,为了保持变换后的图像不失真,就需要对图像进行插值。
常见的插值方法有最近邻插值和双线性插值。
最近邻插值:
最近邻插值是最简单的一种插值方式,就是在对图像进行坐标变换之后,选取原图像中距离最近的像素点进行插值(简单说就是将目标图像的像素位置映射到源图像之后,对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;
}