写opencv的时候用到了resize函数,
看到一个参数INTER_CUBIC
cv::InterpolationFlags::INTER_CUBIC
这个参数就是表示使用双立方插值的方式对图像进行缩放。
然后就去查了一下双立方插值算法。
然后说一下双立方插值怎么计算的:
1、
先给出一张图像src(原图像),大小假设为(m,n)
输出图像定义为dst
输出图像的大小定义为Size size(M,N)
2、
输出图像的dst(i,j)的值怎么计算呢?
首先我们判断一下缩放后的dst(i,j)在原图src中位置在哪?
可以按照计算方式:
float row = i*m/M;
float col = j*n/N;
这里计算出来的位置为(row,col),也就是dst(i,j)在原图中的位置的值是src(row,col),也就是dst(i,j) = src(row,col);
2、
但是row,col都是浮点数,那怎么计算src(row,col)这个值呢?
好吧,这个图1很多地方都有了,这里借用一下。图中暂且可以把p点认为是点(row,col),那这里就是要计算p点的值,
p点的值怎么计算呢,这里是取p点周围的16个像素,类似于下图1,
然后p点的值(影响因子w_ij乘以a_ij,然后求和):
3、
这里上面已经可以计算src(row,col)了,也就可以得到dst(i,j),因为dst(i,j)=src(row,col),
但是上面出现了一个,这个是什么呢,怎么计算呢?
这里w_ij就是上面说的影响因子,图一中4*4图像每个像素对应一个影响因子,总共16个,怎么计算这16个影响因子。
这里要引入一个函数,Bicubic函数,影响因子的计算就是基于这个函数:
通过获取每行(列)像素对应的x就可以获取w_ij了。
4、
所以x(y)的值要怎么计算呢?
这里现获得x_i(行),y_j(列),然后获得w_i,w_j,最后w_ij=w_i*w_j.
//行
x_0 = (1+u);
x_1 = (u);
x_2 = (1-u);
x_3 = (2-u);
//列
y_0 = (1+v);
y_1 = (v);
y_2 = (1-v);
y_3 = (2-v);
//u = row - (int)row
//v = col - (int)col
w_i = w(x_i);//行影响因子
w_j = w(y_j);//列影响因子
w_ij = w_i*w_j;
到这里,就已经获得了所有需要的东西,就可以计算了。
#include
#include
#include
using namespace cv;
//双立方插值函数
float bicubic(float x)
{
float a = 0.25f;
float res = 0.f;
x = fabsf(x);
if (x <= 1.f)
res = (a + 2) * x * x * x - (a + 3) * x * x + 1;
else if (x > 1.f && x <= 2.f)
res = a * x * x * x - 5 * a * x * x + 8 * a * x - 4 * a;
return res;
}
void getImpactFactors(float rowU,float colV,vector& rowImFac,vector& colImFac,int starti,int startj)
{
//取整
int row = (int)rowU;
int col = (int)colV;
//4*4行列影响因子
rowImFac.resize(4);
colImFac.resize(4);
float temp;
//计算行系数因子
for (int i = 0; i < 4; i++)
{
if (starti + i <= 0)
temp = rowU - row - (starti + i);
else
temp = (starti + i) - (rowU - row);
rowImFac[i] = bicubic(temp);
}
//计算列系数因子
for (int j = 0; j < 4; j++)
{
if (startj + j <= 0)
temp = colV - col - (startj + j);
else
temp = (startj + j) - (colV - col);
colImFac[j] = bicubic(temp);
}
}
void myBicubicResize(Mat inputMat, Mat& outputMat, Size size)
{
outputMat = Mat::zeros(size, CV_32FC3);
float inputrows = (float)inputMat.rows;
float inputcols = (float)inputMat.cols;
float outputrows = (float)size.height;
float outputcols = (float)size.width;
Mat tmmat;
//BORDER_DEFAULT = BORDER_REFLECT_101
copyMakeBorder(inputMat, tmmat, 2, 2, 2, 2, BORDER_DEFAULT);
vector rowImFac(4), colImFac(4);
for (int row = 0; row < outputrows; row++)
{
for (int col = 0; col < outputcols; col++)
{
//计算outputMat(row,col)在原图中的位置坐标
float inputrowf = row * (inputrows / outputrows);
float inputcolf = col * (inputcols / outputcols);
//取整
int interow = (int)inputrowf;
int intecol = (int)inputcolf;
float row_dy = inputrowf - interow;
float col_dx = inputcolf - intecol;
//因为扩展了边界,所以+2
interow += 2;
intecol += 2;
int starti = -1, startj = -1;
//计算行影响因子,列影响因子
getImpactFactors(inputrowf, inputcolf, rowImFac, colImFac,starti,startj);
//计算输出图像(row,col)的值
Vec3f tempvec(0, 0, 0);
for (int i = starti; i < starti+4; i++)
{
for (int j = startj; j < startj+4; j++)
{
tempvec += (Vec3f)(tmmat.at(interow + i, intecol + j) * rowImFac[i - starti] * colImFac[j - startj]);
}
}
outputMat.at(row, col) = tempvec;
}
}
}
int main(int argc, char** argv)
{
Mat srcmat = cv::imread("Lena.png");
if (srcmat.empty())
return 0;
Mat dst;
Mat dst1;
Size size(500, 256);
myBicubicResize(srcmat, dst, size);
dst.convertTo(dst, CV_8UC3);
resize(srcmat, dst1, size, 0, 0, INTER_CUBIC);
imshow("dst", dst);
imshow("dst1", dst1);
waitKey(0);
return 0;
}
这里要说一点,opencv里面a取的是-0.75,我这里取的是0.25,
这里我试验过几次,我的a值取-0.5或者-0.75的时候,会出现问题,会有一些竖向的痕迹,
a值取0.25或者0.5的时候就没有问题。
除此之外,如果a值取-0.5或者-0.75,并且图像size(col,row)以相同比例缩放,也不会出现竖向的痕迹。
总的来说不是太复杂,,,,
不过还有很多需要学习的地方,
参考的文章:(除了a的取值不同,自己还做了一些其他的试验,比如最近的16个点)
https://blog.csdn.net/qq_29058565/article/details/52769497