插值算法

線性插值


線性插值

已知兩點(diǎn) A(x_1, y_1)B(x_2, y_2),以及插值參數(shù) t (0 \leq t \leq 1),可以通過(guò)以下公式計(jì)算任意中間點(diǎn) C(x_t, y_t) 的坐標(biāo)。

  1. x 坐標(biāo)的插值:
    中間點(diǎn)的 x 坐標(biāo) x_tABx 坐標(biāo)按照比例 t 插值的結(jié)果,公式為:
    x_t = (1 - t) \cdot x_1 + t \cdot x_2

    • 當(dāng) t = 0 時(shí),x_t = x_1。
    • 當(dāng) t = 1 時(shí),x_t = x_2。
    • 0 \leq t \leq 1 的范圍內(nèi),x_t 線性分布在 x_1x_2 之間。
  2. y 坐標(biāo)的插值:
    中間點(diǎn)的 y 坐標(biāo) y_tABy 坐標(biāo)按照比例 t 插值的結(jié)果,公式為:
    y_t = (1 - t) \cdot y_1 + t \cdot y_2

    • 當(dāng) t = 0 時(shí),y_t = y_1。
    • 當(dāng) t = 1 時(shí),y_t = y_2。
    • 0 \leq t \leq 1 的范圍內(nèi),y_t 線性分布在 y_1y_2 之間。
  3. y 坐標(biāo)的插值:
    中間點(diǎn)的 y 坐標(biāo) y_tAB 的 y 坐標(biāo)按照比例 t 插值的結(jié)果,公式為:
    y_t = (1 - t) \cdot y_1 + t \cdot y_2

  4. 綜合表示:
    中間點(diǎn) C(x_t, y_t) 的坐標(biāo)公式為:
    C(x_t, y_t) = \big((1 - t) \cdot x_1 + t \cdot x_2, \, (1 - t) \cdot y_1 + t \cdot y_2 \big)

雙線性插值 bilinear

雙線性插值

雙線性插值公式推導(dǎo)

雙線性插值是一種常見(jiàn)的插值方法,主要用于二維網(wǎng)格中根據(jù)已知點(diǎn)的值來(lái)估算某個(gè)未知點(diǎn)的值。


1. 問(wèn)題描述

給定四個(gè)已知網(wǎng)格點(diǎn) Q_0, Q_1, Q_2, Q_3 的值 z_{00}, z_{10}, z_{01}, z_{11},以及目標(biāo)插值點(diǎn) P(tx, ty) 的相對(duì)位置,要求插值點(diǎn) P(tx, ty) 的值 z。在這里剛好m=tx, n=ty,一般來(lái)說(shuō)tx,tym,n的小數(shù)部分

  • 四個(gè)網(wǎng)格點(diǎn)的坐標(biāo)分別為:
    Q_0: (0, 0), \quad Q_1: (1, 0), \quad Q_2: (0, 1), \quad Q_3: (1, 1)
  • 插值點(diǎn) P(tx, ty) 的坐標(biāo)為:
    P(tx, ty), \quad 其中 tx, ty \in [0, 1]

目標(biāo)是根據(jù) tx, ty 對(duì) z 進(jìn)行估算。


2. 計(jì)算 P_1P_2

步驟 1:對(duì) x 方向插值

Q_0Q_1 之間插值,計(jì)算 P_1(tx, 0) 的值:
P_1(tx, 0) = z_{00} \cdot (1 - tx) + z_{10} \cdot tx

同理,在 Q_2Q_3 之間插值,計(jì)算 P_2(tx, 1) 的值:
P_2(tx, 1) = z_{01} \cdot (1 - tx) + z_{11} \cdot tx


3. 根據(jù) P_1P_2 計(jì)算 P(tx, ty)

步驟 2:對(duì) y 方向插值

P_1(tx, 0)P_2(tx, 1) 之間插值,計(jì)算 P(tx, ty) 的值:
P(tx, ty) = P_1(tx, 0) \cdot (1 - ty) + P_2(tx, 1) \cdot ty

P_1(tx, 0)P_2(tx, 1) 的表達(dá)式代入上式,得到:
P(tx, ty) = \big[ z_{00} \cdot (1 - tx) + z_{10} \cdot tx \big] \cdot (1 - ty) + \big[ z_{01} \cdot (1 - tx) + z_{11} \cdot tx \big] \cdot ty

展開(kāi)化簡(jiǎn):
P(tx, ty) = z_{00} \cdot (1 - tx) \cdot (1 - ty) + z_{10} \cdot tx \cdot (1 - ty) + z_{01} \cdot (1 - tx) \cdot ty + z_{11} \cdot tx \cdot ty


4. 雙線性插值公式

最終公式為:
P(tx, ty) = z_{00} \cdot (1 - tx) \cdot (1 - ty) + z_{10} \cdot tx \cdot (1 - ty) + z_{01} \cdot (1 - tx) \cdot ty + z_{11} \cdot tx \cdot ty


5. 總結(jié)

雙線性插值公式分兩步:

  1. x 方向插值,計(jì)算 P_1P_2 的值。
  2. y 方向插值,根據(jù) P_1P_2 的值計(jì)算目標(biāo)點(diǎn) P(tx, ty)。

這是一種對(duì)二維網(wǎng)格點(diǎn)的插值方法,假設(shè) xy 方向是線性變化的,因此公式基于線性插值的原則推導(dǎo)而來(lái)。

用面積理解雙線性插值示意圖

面積理解雙線性插值

雙三次插值 bicubic


雙三次插值

步驟 1:計(jì)算 x 方向的中間插值點(diǎn) P_0, P_1, P_2, P_3

  1. 確定插值點(diǎn)和網(wǎng)格點(diǎn)

    • 插值點(diǎn)為 P(n, m),位于 (n, m)。
    • 最近的左上角網(wǎng)格點(diǎn)為 Q_{11} = (\lfloor n \rfloor, \lfloor m \rfloor)
    • 選擇 P 點(diǎn)周?chē)?4 \times 4 網(wǎng)格點(diǎn):
      q_{i,j} = (\lfloor n \rfloor + j, \lfloor m \rfloor + i), \quad i, j \in \{-1, 0, 1, 2\}
  2. 計(jì)算 x 方向的偏移

    • 計(jì)算 t_x = n - \lfloor n \rfloor,即插值點(diǎn)在 x 方向上相對(duì)于網(wǎng)格點(diǎn)的偏移。
  3. 計(jì)算 x 方向的中間值 P_0, P_1, P_2, P_3

    • 對(duì)于每一行 i \in \{-1, 0, 1, 2\},使用 x 方向的插值權(quán)重 w_x(j, t_x) 計(jì)算中間插值點(diǎn):
      P_i = \sum_{j=-1}^{2} w_x(j, t_x) \cdot q_{i,j}
    • w_x(j, t_x) 的權(quán)重函數(shù) w(x) 定義如下(見(jiàn)權(quán)重公式部分)。

步驟 2:計(jì)算 y 方向的最終插值點(diǎn) P

  1. 計(jì)算 y 方向的偏移

    • 計(jì)算 t_y = m - \lfloor m \rfloor,即插值點(diǎn)在 y 方向上相對(duì)于網(wǎng)格點(diǎn)的偏移。
  2. 計(jì)算 y 方向的插值值 P

    • 使用 y 方向的插值權(quán)重 w_y(i, t_y) 和中間插值點(diǎn) P_0, P_1, P_2, P_3 計(jì)算最終插值點(diǎn)的值:
      P(n, m) = \sum_{i=-1}^{2} w_y(i, t_y) \cdot P_i

雙三次插值的權(quán)重函數(shù)

權(quán)重函數(shù) w(x) 的定義為:
w(x) = \begin{cases} 1.5 |x|^3 - 2.5 |x|^2 + 1, & \text{if } 0 \leq |x| < 1 \\ -0.5 |x|^3 + 2.5 |x|^2 - 4 |x| + 2, & \text{if } 1 \leq |x| < 2 \\ 0, & \text{if } |x| \geq 2 \end{cases}

權(quán)重函數(shù)中的變量說(shuō)明

  • x 表示插值點(diǎn)與網(wǎng)格點(diǎn)之間的相對(duì)距離,定義為:
    x = j - t_x \quad \text{或} \quad x = i - t_y
  • 根據(jù) x 的絕對(duì)值范圍,選擇對(duì)應(yīng)的公式計(jì)算權(quán)重。

總結(jié)步驟

  1. 計(jì)算 x 方向的中間插值點(diǎn)
    • 使用 t_xw(x) 對(duì)每一行進(jìn)行插值,得到 P_0, P_1, P_2, P_3。
  2. 計(jì)算 y 方向的最終插值點(diǎn)
    • 使用 t_yw(x) 對(duì) P_0, P_1, P_2, P_3 進(jìn)行插值,得到插值點(diǎn) P。
  3. 核心公式
    • x 方向中間值:
      P_i = \sum_{j=-1}^{2} w_x(j, t_x) \cdot q_{i,j}
    • y 方向最終值:
      P(n, m) = \sum_{i=-1}^{2} w_y(i, t_y) \cdot P_i
  4. 權(quán)重函數(shù)
    w(x) = \begin{cases} 1.5 |x|^3 - 2.5 |x|^2 + 1, & \text{if } 0 \leq |x| < 1 \\ -0.5 |x|^3 + 2.5 |x|^2 - 4 |x| + 2, & \text{if } 1 \leq |x| < 2 \\ 0, & \text{if } |x| \geq 2 \end{cases}

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);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容