線性插值

已知兩點(diǎn)
-
x 坐標(biāo)的插值:
中間點(diǎn)的 x 坐標(biāo)是
和
的
坐標(biāo)按照比例
插值的結(jié)果,公式為:
- 當(dāng)
時(shí),
。
- 當(dāng)
時(shí),
。
- 在
的范圍內(nèi),
線性分布在
和
之間。
- 當(dāng)
-
y 坐標(biāo)的插值:
中間點(diǎn)的坐標(biāo)
是
和
的
坐標(biāo)按照比例
插值的結(jié)果,公式為:
- 當(dāng)
時(shí),
。
- 當(dāng)
時(shí),
。
- 在
的范圍內(nèi),
線性分布在
和
之間。
- 當(dāng)
y 坐標(biāo)的插值:
中間點(diǎn)的 y 坐標(biāo)是
和
的 y 坐標(biāo)按照比例
插值的結(jié)果,公式為:
綜合表示:
中間點(diǎn)的坐標(biāo)公式為:
雙線性插值 bilinear

雙線性插值公式推導(dǎo)
雙線性插值是一種常見(jiàn)的插值方法,主要用于二維網(wǎng)格中根據(jù)已知點(diǎn)的值來(lái)估算某個(gè)未知點(diǎn)的值。
1. 問(wèn)題描述
給定四個(gè)已知網(wǎng)格點(diǎn) 的值
,以及目標(biāo)插值點(diǎn)
的相對(duì)位置,要求插值點(diǎn)
的值
。在這里剛好
,一般來(lái)說(shuō)
是
的小數(shù)部分
- 四個(gè)網(wǎng)格點(diǎn)的坐標(biāo)分別為:
- 插值點(diǎn)
的坐標(biāo)為:
目標(biāo)是根據(jù) 對(duì)
進(jìn)行估算。
2. 計(jì)算
和
步驟 1:對(duì) 方向插值
在 和
之間插值,計(jì)算
的值:
同理,在 和
之間插值,計(jì)算
的值:
3. 根據(jù)
和
計(jì)算
步驟 2:對(duì) 方向插值
在 和
之間插值,計(jì)算
的值:
將 和
的表達(dá)式代入上式,得到:
展開(kāi)化簡(jiǎn):
4. 雙線性插值公式
最終公式為:
5. 總結(jié)
雙線性插值公式分兩步:
-
方向插值,計(jì)算
和
的值。
-
方向插值,根據(jù)
和
的值計(jì)算目標(biāo)點(diǎn)
。
這是一種對(duì)二維網(wǎng)格點(diǎn)的插值方法,假設(shè) 和
方向是線性變化的,因此公式基于線性插值的原則推導(dǎo)而來(lái)。
用面積理解雙線性插值示意圖

雙三次插值 bicubic

步驟 1:計(jì)算
方向的中間插值點(diǎn)
-
確定插值點(diǎn)和網(wǎng)格點(diǎn)
- 插值點(diǎn)為
,位于
。
- 最近的左上角網(wǎng)格點(diǎn)為
。
- 選擇
點(diǎn)周?chē)?
網(wǎng)格點(diǎn):
- 插值點(diǎn)為
-
計(jì)算
方向的偏移
- 計(jì)算
,即插值點(diǎn)在
方向上相對(duì)于網(wǎng)格點(diǎn)的偏移。
- 計(jì)算
-
計(jì)算
方向的中間值
- 對(duì)于每一行
,使用
方向的插值權(quán)重
計(jì)算中間插值點(diǎn):
-
的權(quán)重函數(shù)
定義如下(見(jiàn)權(quán)重公式部分)。
- 對(duì)于每一行
步驟 2:計(jì)算
方向的最終插值點(diǎn)
-
計(jì)算
方向的偏移
- 計(jì)算
,即插值點(diǎn)在
方向上相對(duì)于網(wǎng)格點(diǎn)的偏移。
- 計(jì)算
-
計(jì)算
方向的插值值
- 使用
方向的插值權(quán)重
和中間插值點(diǎn)
計(jì)算最終插值點(diǎn)的值:
- 使用
雙三次插值的權(quán)重函數(shù)
權(quán)重函數(shù) 的定義為:
權(quán)重函數(shù)中的變量說(shuō)明
-
表示插值點(diǎn)與網(wǎng)格點(diǎn)之間的相對(duì)距離,定義為:
- 根據(jù)
的絕對(duì)值范圍,選擇對(duì)應(yīng)的公式計(jì)算權(quán)重。
總結(jié)步驟
-
計(jì)算
方向的中間插值點(diǎn)
- 使用
和
對(duì)每一行進(jìn)行插值,得到
。
- 使用
-
計(jì)算
方向的最終插值點(diǎn)
- 使用
和
對(duì)
進(jìn)行插值,得到插值點(diǎn)
。
- 使用
-
核心公式
-
方向中間值:
-
方向最終值:
-
-
權(quán)重函數(shù):
cuda 仿射變換實(shí)現(xiàn)
雙線性插值
static .,__device__ float bicubic_weight(float x) {
const float a = -0.5f; // 常見(jiàn)的值,通常取-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
{
// 直接在插值時(shí)獲取像素值
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;
// 邊界檢查并計(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 方向插值并輸出到目標(biāo)圖像
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; // 常見(jiàn)的值,通常取-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
{
// 直接在插值時(shí)獲取像素值
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;
// 邊界檢查并計(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 方向插值并輸出到目標(biāo)圖像
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);
}
}