1. 程式人生 > >影象插值演算法的原理及C++實現

影象插值演算法的原理及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;
}