图像的放大:双三次插值算法(C++实现)

双线性插值算法的不足就是细节处理的不好,换句话说,就是曲线拟合得不够光滑,所以又有了双三次插值算法。双三次插值算法是基于周围的16个像素点,通过计算16个像素点的权重,累积得到增加点的像素值的。
在这里插入图片描述
简单点理解,公式如下:
p = f ( u , v ) = ∑ i = 0 3 ∑ j = 0 3 w i j x i y j p=f(u,v)=\sum_{i=0}^{3}\sum_{j=0}^{3}w_{ij}x_{i}y_{j} p=f(u,v)=i=03j=03wijxiyj
(u,v)为所插入点的坐标,(x,y)为周围16个像素点的坐标。本算法的重点和难点就是权重w的值如何获取。我们取其中的x方向(横轴)分析, w x i w_{xi} wxi的取值如下公式所示:
w x i = { ( a + 2 ) ∣ x i − u ∣ 3 − ( a + 3 ) ∣ x i − u ∣ 2 + 1 f o r ∣ x i − u ∣ ≤ 1 a ∣ x i − u ∣ 3 − 5 a ∣ x i − u ∣ 2 + 8 a ∣ x i − u ∣ − 4 a f o r 1 < ∣ x i − u ∣ < 2 0 o t h e r w i s e w_{xi}=\left\{\begin{matrix} (a+2)\left | x_i-u \right |^{3}-(a+3)\left | x_i-u \right |^{2}+1 & for & \left | x_i-u \right |\leq 1\\ a\left | x_i-u \right |^{3}-5a\left | x_i-u \right |^{2}+8a\left | x_i-u \right |-4a & for &1< \left | x_i-u \right |< 2 \\ 0 &otherwise \end{matrix}\right. wxi=(a+2)xiu3(a+3)xiu2+1axiu35axiu2+8axiu4a0forforotherwisexiu11<xiu<2
同理,也可以得到 w y j w_{yj} wyj,则可以得到权重的表达式如下公式所示:
w i j = w x i × w y j w_{ij}=w_{xi}\times w_{yj} wij=wxi×wyj
c++代码实现:
"bmp.h"的头文件代码:bmp图片的类以及读写函数 bmp.h

#include <iostream>
#include <string.h>   
#include <math.h>       
#include <stdlib.h>     
#include <malloc.h>  
#include <cstdio>
#include "bmp.h"
using namespace std;
#define DRAW_HEIGHT 512  //目标图像高度  
#define DRAW_WIDTH 512  //目标图像宽度  
double a=-0.5; //BiCubic基函数

void getW_x(double w_x[4],double x);
void getW_y(double w_y[4], double y);

int main()
{
    BMP rbmp;
    BMP wbmp(DRAW_WIDTH,DRAW_HEIGHT);
	char strFile[50] = "./lena24.bmp";//打开图像路径,BMP图像必须为24位真彩色格式  
	char strFilesave[50] = "./test.bmp";//处理后图像存储路径 
    //读取位图的数据 
    imread(strFile,rbmp);
    int width = rbmp.cols();
	int height = rbmp.rows(); 
	int l_width = WIDTHBYTES(width*24);//计算位图的实际宽度并确保它为4byte的倍数  
    //写位图的数据
    int write_width = WIDTHBYTES(DRAW_WIDTH*24);//计算写位图的实际宽度(字节)并确保它为4byte的倍数   
	/*******************图像处理部分******************/
    for (int hnum = 2; hnum < DRAW_HEIGHT-4; hnum++)
	{
    	for (int wnum = 2; wnum < DRAW_WIDTH-4; wnum++)
		{
			double d_original_img_hnum = hnum*height / (double)DRAW_HEIGHT;
			double d_original_img_wnum = wnum*width / (double)DRAW_WIDTH;
			int i_original_img_hnum = d_original_img_hnum;//距离最近的点坐标
			int i_original_img_wnum = d_original_img_wnum;//距离最近的点坐标
            double w_x[4], w_y[4];//行列方向的加权系数
            getW_x(w_x, d_original_img_hnum);
            getW_y(w_y, d_original_img_wnum);
            int pixel_point = hnum*write_width + wnum * 3;//映射尺度变换图像数组位置偏移量  

            for(int i=0;i<4;i++)
            {
                for(int j=0;j<4;j++)
                {
                    int original_point=(i_original_img_hnum+i-1)*l_width + (i_original_img_wnum+j-1)*3;
                    wbmp.pColorData[pixel_point]+=rbmp.pColorData[original_point]*w_x[i]*w_y[j];
                    wbmp.pColorData[pixel_point + 1]+=rbmp.pColorData[original_point+1]*w_x[i]*w_y[j];
                    wbmp.pColorData[pixel_point + 2]+=rbmp.pColorData[original_point+2]*w_x[i]*w_y[j];
                }
            }
		}
    }
	/*******************图像处理部分******************/
    std::cout<<"Done!"<<std::endl;
    imwrite(strFilesave,wbmp); 
    system("pause"); 
    return 0;
}
/*计算系数*/
void getW_x(double w_x[4],double x){
    int X = (int)x;//取整数部分
    double stemp_x[4];
    stemp_x[0] = 1 + (x - X);
    stemp_x[1] = x - X;
    stemp_x[2] = 1 - (x - X);
    stemp_x[3] = 2 - (x - X);

    w_x[0] = a*abs(stemp_x[0] * stemp_x[0] * stemp_x[0]) - 5 * a*stemp_x[0] * stemp_x[0] + 8 * a*abs(stemp_x[0]) - 4 * a;
    w_x[1] = (a + 2)*abs(stemp_x[1] * stemp_x[1] * stemp_x[1]) - (a + 3)*stemp_x[1] * stemp_x[1] + 1;
    w_x[2] = (a + 2)*abs(stemp_x[2] * stemp_x[2] * stemp_x[2]) - (a + 3)*stemp_x[2] * stemp_x[2] + 1;
    w_x[3] = a*abs(stemp_x[3] * stemp_x[3] * stemp_x[3]) - 5 * a*stemp_x[3] * stemp_x[3] + 8 * a*abs(stemp_x[3]) - 4 * a;
}
void getW_y(double w_y[4], double y){
    int Y = (int)y;
    double stemp_y[4];
    stemp_y[0] = 1.0 + (y - Y);
    stemp_y[1] = y - Y;
    stemp_y[2] = 1 - (y - Y);
    stemp_y[3] = 2 - (y - Y);

    w_y[0] = a*abs(stemp_y[0] * stemp_y[0] * stemp_y[0]) - 5 * a*stemp_y[0] * stemp_y[0] + 8 * a*abs(stemp_y[0]) - 4 * a;
    w_y[1] = (a + 2)*abs(stemp_y[1] * stemp_y[1] * stemp_y[1]) - (a + 3)*stemp_y[1] * stemp_y[1] + 1;
    w_y[2] = (a + 2)*abs(stemp_y[2] * stemp_y[2] * stemp_y[2]) - (a + 3)*stemp_y[2] * stemp_y[2] + 1;
    w_y[3] = a*abs(stemp_y[3] * stemp_y[3] * stemp_y[3]) - 5 * a*stemp_y[3] * stemp_y[3] + 8 * a*abs(stemp_y[3]) - 4 * a;
}

效果图:
在这里插入图片描述
在这里插入图片描述
参考:图像缩放之双三次插值法

  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
好的,以下是用C++实现双三次插值算法: ```c++ #include <iostream> #include <vector> #include <cmath> using namespace std; double bicubicInterpolation(double x, double y, const vector<vector<double>>& data) { int nx = data.size(); int ny = data[0].size(); int i = floor(x); int j = floor(y); double u = x - i; double v = y - j; double f[4][4]; for (int di = 0; di < 4; di++) { for (int dj = 0; dj < 4; dj++) { int ni = i + di - 1; int nj = j + dj - 1; ni = max(0, min(ni, nx - 1)); nj = max(0, min(nj, ny - 1)); f[di][dj] = data[ni][nj]; } } double wx[4], wy[4]; for (int di = 0; di < 4; di++) { double p = u * u * u; double q = u * u; wx[di] = (di == 0) ? p - 2 * q + u : -p + 2 * q - u; } for (int dj = 0; dj < 4; dj++) { double p = v * v * v; double q = v * v; wy[dj] = (dj == 0) ? p - 2 * q + v : -p + 2 * q - v; } double result = 0; for (int di = 0; di < 4; di++) { for (int dj = 0; dj < 4; dj++) { result += wx[di] * wy[dj] * f[di][dj]; } } return result; } int main() { vector<vector<double>> data = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16} }; double x = 1.5; double y = 2.5; double result = bicubicInterpolation(x, y, data); cout << "Interpolated value at (" << x << ", " << y << ") is " << result << endl; return 0; } ``` 这里假设已经有了一个二维数组`data`,表示待插值的数据。`bicubicInterpolation`函数接受两个坐标值和数据数组,返回插值结果。在函数内部,首先找到距离该坐标最近的4x4个点,然后根据双三次插值的公式计算出插值结果。该算法的时间复杂度为O(1)。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值