线性插值
已知两点 和 ,以及插值参数 ,可以通过以下公式计算任意中间点 的坐标。
-
x 坐标的插值:
中间点的 x 坐标 是 和 的 坐标按照比例 插值的结果,公式为:
- 当 时,。
- 当 时,。
- 在 的范围内, 线性分布在 和 之间。
-
y 坐标的插值:
中间点的 坐标 是 和 的 坐标按照比例 插值的结果,公式为:
- 当 时,。
- 当 时,。
- 在 的范围内, 线性分布在 和 之间。
y 坐标的插值:
中间点的 y 坐标 是 和 的 y 坐标按照比例 插值的结果,公式为:
综合表示:
中间点 的坐标公式为:
双线性插值 bilinear
双线性插值公式推导
双线性插值是一种常见的插值方法,主要用于二维网格中根据已知点的值来估算某个未知点的值。
1. 问题描述
给定四个已知网格点 的值 ,以及目标插值点 的相对位置,要求插值点 的值 。在这里刚好,一般来说是的小数部分
- 四个网格点的坐标分别为:
- 插值点 的坐标为:
目标是根据 对 进行估算。
2. 计算 和
步骤 1:对 方向插值
在 和 之间插值,计算 的值:
同理,在 和 之间插值,计算 的值:
3. 根据 和 计算
步骤 2:对 方向插值
在 和 之间插值,计算 的值:
将 和 的表达式代入上式,得到:
展开化简:
4. 双线性插值公式
最终公式为:
5. 总结
双线性插值公式分两步:
- 方向插值,计算 和 的值。
- 方向插值,根据 和 的值计算目标点 。
这是一种对二维网格点的插值方法,假设 和 方向是线性变化的,因此公式基于线性插值的原则推导而来。
用面积理解双线性插值示意图
双三次插值 bicubic
步骤 1:计算 方向的中间插值点
-
确定插值点和网格点
- 插值点为 ,位于 。
- 最近的左上角网格点为 。
- 选择 点周围的 网格点:
-
计算 方向的偏移
- 计算 ,即插值点在 方向上相对于网格点的偏移。
-
计算 方向的中间值
- 对于每一行 ,使用 方向的插值权重 计算中间插值点:
- 的权重函数 定义如下(见权重公式部分)。
- 对于每一行 ,使用 方向的插值权重 计算中间插值点:
步骤 2:计算 方向的最终插值点
-
计算 方向的偏移
- 计算 ,即插值点在 方向上相对于网格点的偏移。
-
计算 方向的插值值
- 使用 方向的插值权重 和中间插值点 计算最终插值点的值:
- 使用 方向的插值权重 和中间插值点 计算最终插值点的值:
双三次插值的权重函数
权重函数 的定义为:
权重函数中的变量说明
-
表示插值点与网格点之间的相对距离,定义为:
- 根据 的绝对值范围,选择对应的公式计算权重。
总结步骤
-
计算 方向的中间插值点
- 使用 和 对每一行进行插值,得到 。
-
计算 方向的最终插值点
- 使用 和 对 进行插值,得到插值点 。
-
核心公式
-
方向中间值:
-
方向最终值:
-
方向中间值:
-
权重函数:
cuda 仿射变换实现
双线性插值
static .,__device__ float bicubic_weight(float x) {
const float a = -0.5f; // 常见的值,通常取-0.5f
if (x < 0) x = -x;
if (x < 1) return (a + 2.0f) * powf(x, 3) - (a + 3.0f) * powf(x, 2) + 1.0f;
if (x < 2) return a * powf(x, 3) - 5.0f * a * powf(x, 2) + 8.0f * a * x - 4.0f * a;
return 0.0f;
}
static __global__ void warp_affine_bicubic_plane_kernel(uint8_t *src,
int src_line_size,
int src_width,
int src_height,
uint8_t *dst,
int dst_width,
int dst_height,
uint8_t const_value_st,
float *warp_affine_matrix_2_3)
{
int dx = blockDim.x * blockIdx.x + threadIdx.x;
int dy = blockDim.y * blockIdx.y + threadIdx.y;
if (dx >= dst_width || dy >= dst_height)
return;
float m_x1 = warp_affine_matrix_2_3[0];
float m_y1 = warp_affine_matrix_2_3[1];
float m_z1 = warp_affine_matrix_2_3[2];
float m_x2 = warp_affine_matrix_2_3[3];
float m_y2 = warp_affine_matrix_2_3[4];
float m_z2 = warp_affine_matrix_2_3[5];
float src_x = m_x1 * dx + m_y1 * dy + m_z1;
float src_y = m_x2 * dx + m_y2 * dy + m_z2;
if (src_x <= 1 || src_x >= src_width - 2 || src_y <= 1 || src_y >= src_height - 2)
{
dst[(dy * dst_width + dx) * 3] = const_value_st;
dst[(dy * dst_width + dx) * 3 + 1] = const_value_st;
dst[(dy * dst_width + dx) * 3 + 2] = const_value_st;
}
else
{
// 直接在插值时获取像素值
float wts_x[4], wts_y[4];
for (int i = 0; i < 4; i++)
{
wts_x[i] = bicubic_weight(src_x - ((int)floorf(src_x) - 1 + i));
wts_y[i] = bicubic_weight(src_y - ((int)floorf(src_y) - 1 + i));
}
// x 方向插值
float p_x[4][3] = {{0}};
for (int i = 0; i < 4; i++)
{
float px_r = 0, px_g = 0, px_b = 0;
for (int j = 0; j < 4; j++)
{
int y = (int)floorf(src_y) - 1 + i;
int x = (int)floorf(src_x) - 1 + j;
// 边界检查并计算像素
if (y >= 0 && y < src_height && x >= 0 && x < src_width)
{
uint8_t* pixel = &src[y * src_line_size + x * 3];
px_r += pixel[0] * bicubic_weight(src_x - x);
px_g += pixel[1] * bicubic_weight(src_x - x);
px_b += pixel[2] * bicubic_weight(src_x - x);
}
else
{
px_r += const_value_st * bicubic_weight(src_x - x);
px_g += const_value_st * bicubic_weight(src_x - x);
px_b += const_value_st * bicubic_weight(src_x - x);
}
}
p_x[i][0] = px_r;
p_x[i][1] = px_g;
p_x[i][2] = px_b;
}
// y 方向插值并输出到目标图像
dst[(dy * dst_width + dx) * 3] = (uint8_t)min(max(wts_y[0] * p_x[0][0] + wts_y[1] * p_x[1][0] + wts_y[2] * p_x[2][0] + wts_y[3] * p_x[3][0] + 0.5f, 0.0f), 255.0f);
dst[(dy * dst_width + dx) * 3 + 1] = (uint8_t)min(max(wts_y[0] * p_x[0][1] + wts_y[1] * p_x[1][1] + wts_y[2] * p_x[2][1] + wts_y[3] * p_x[3][1] + 0.5f, 0.0f), 255.0f);
dst[(dy * dst_width + dx) * 3 + 2] = (uint8_t)min(max(wts_y[0] * p_x[0][2] + wts_y[1] * p_x[1][2] + wts_y[2] * p_x[2][2] + wts_y[3] * p_x[3][2] + 0.5f, 0.0f), 255.0f);
}
}
双三次插值
static __device__ float bicubic_weight(float x) {
const float a = -0.5f; // 常见的值,通常取-0.5f
if (x < 0) x = -x;
if (x < 1) return (a + 2.0f) * powf(x, 3) - (a + 3.0f) * powf(x, 2) + 1.0f;
if (x < 2) return a * powf(x, 3) - 5.0f * a * powf(x, 2) + 8.0f * a * x - 4.0f * a;
return 0.0f;
}
static __global__ void warp_affine_bicubic_plane_kernel(uint8_t *src,
int src_line_size,
int src_width,
int src_height,
uint8_t *dst,
int dst_width,
int dst_height,
uint8_t const_value_st,
float *warp_affine_matrix_2_3)
{
int dx = blockDim.x * blockIdx.x + threadIdx.x;
int dy = blockDim.y * blockIdx.y + threadIdx.y;
if (dx >= dst_width || dy >= dst_height)
return;
float m_x1 = warp_affine_matrix_2_3[0];
float m_y1 = warp_affine_matrix_2_3[1];
float m_z1 = warp_affine_matrix_2_3[2];
float m_x2 = warp_affine_matrix_2_3[3];
float m_y2 = warp_affine_matrix_2_3[4];
float m_z2 = warp_affine_matrix_2_3[5];
float src_x = m_x1 * dx + m_y1 * dy + m_z1;
float src_y = m_x2 * dx + m_y2 * dy + m_z2;
if (src_x <= 1 || src_x >= src_width - 2 || src_y <= 1 || src_y >= src_height - 2)
{
dst[(dy * dst_width + dx) * 3] = const_value_st;
dst[(dy * dst_width + dx) * 3 + 1] = const_value_st;
dst[(dy * dst_width + dx) * 3 + 2] = const_value_st;
}
else
{
// 直接在插值时获取像素值
float wts_y[4];
for (int i = 0; i < 4; i++)
{
wts_y[i] = bicubic_weight(src_y - ((int)floorf(src_y) - 1 + i));
}
// x 方向插值
float p_x[4][3] = {{0}};
for (int i = 0; i < 4; i++)
{
float px_r = 0, px_g = 0, px_b = 0;
for (int j = 0; j < 4; j++)
{
int y = (int)floorf(src_y) - 1 + i;
int x = (int)floorf(src_x) - 1 + j;
// 边界检查并计算像素
if (y >= 0 && y < src_height && x >= 0 && x < src_width)
{
uint8_t* pixel = &src[y * src_line_size + x * 3];
px_r += pixel[0] * bicubic_weight(src_x - x);
px_g += pixel[1] * bicubic_weight(src_x - x);
px_b += pixel[2] * bicubic_weight(src_x - x);
}
else
{
px_r += const_value_st * bicubic_weight(src_x - x);
px_g += const_value_st * bicubic_weight(src_x - x);
px_b += const_value_st * bicubic_weight(src_x - x);
}
}
p_x[i][0] = px_r;
p_x[i][1] = px_g;
p_x[i][2] = px_b;
}
// y 方向插值并输出到目标图像
dst[(dy * dst_width + dx) * 3] = (uint8_t)min(max(wts_y[0] * p_x[0][0] + wts_y[1] * p_x[1][0] + wts_y[2] * p_x[2][0] + wts_y[3] * p_x[3][0] + 0.5f, 0.0f), 255.0f);
dst[(dy * dst_width + dx) * 3 + 1] = (uint8_t)min(max(wts_y[0] * p_x[0][1] + wts_y[1] * p_x[1][1] + wts_y[2] * p_x[2][1] + wts_y[3] * p_x[3][1] + 0.5f, 0.0f), 255.0f);
dst[(dy * dst_width + dx) * 3 + 2] = (uint8_t)min(max(wts_y[0] * p_x[0][2] + wts_y[1] * p_x[1][2] + wts_y[2] * p_x[2][2] + wts_y[3] * p_x[3][2] + 0.5f, 0.0f), 255.0f);
}
}