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