图像缩放-双线性插值

边缘计算
Published

April 17, 2019

需要在k210中实现图像的裁剪缩放,所以就写了个双线性插值的程序进行处理.写好了记录一下.

理论

双线性插值只涉及到邻近的4个像素点,如图:

通过一个比例系数算出最终图像的(distI,distJ)在源图像中的具体位置,这个位置肯定会有在两个像素点中间,这个时候就可以通过双线性插值的方式确定像素值.

\[ \begin{aligned} z_1&=(f(i,j+1)-f(i,j))*u+f(i,j) \\ z_2&=(f(i+1,j+1)-f(i+1,j))*u+f(i+1,j) \\ z &=(z_2-z_1)*v+z_1 \\ \\ f(x,y) &: x,y对应位置的像素值 \\ u &: 映射像素在横向两个像素之间的位置 \\ v &: 映射像素在纵向两个像素之间的位置 \end{aligned} \]

NOTE: 这里其实先算\(u\)还是\(v\)都没有关系,但是要注意先算\(v\)的话需要纵向相减.还有放大缩小的坐标需要通过\((i+0.5)*r-0.5\)来计算,这样才可以保证映射到中心对齐.

程序

两段小程序:

这是resize rgb888 hwc格式的图像

/**
 * @brief resize rgb image, image : [old_h,old_h,3]
 *          NOTE : image have to ba rag888
 *
 * @param old_img
 * @param new_img
 * @param old_h
 * @param old_w
 * @param new_h
 * @param new_w
 */
void resize_img(uint8_t *old_img, uint8_t *new_img, int old_h, int old_w, int new_h, int new_w) {
    if (old_img == nullptr || new_img == nullptr) { return; }

    float rx= (float)old_w / new_w, ry= (float)old_h / new_h;
    float rgx[new_w], rgy[new_h];
    int fx0y0, fx0y1, fx1y0, fx1y1;
    uint x0, y0, x1, y1;
    uint z1, z2, z, cnl= 3, old_stride= old_w * cnl, new_stride= new_w * cnl;
    for (size_t i= 0; i < new_w; i++) { rgx[i]= (i + 0.5) * rx - 0.5; }
    for (size_t i= 0; i < new_h; i++) { rgy[i]= (i + 0.5) * ry - 0.5; }

    for (size_t i= 0; i < new_h; i++) {
        y0= (uint)rgy[i];
        y1= (y0 + 1) < old_h ? (y0 + 1) : old_h;
        for (size_t j= 0; j < new_w; j++) {
            x0= (uint)rgx[j];
            x1= (x0 + 1) < old_w ? (x0 + 1) : old_w;

            float u= rgx[j] - x0, v= rgy[i] - y0;
            for (size_t k= 0; k < cnl; k++) {
                fx0y0= rgb_image[old_stride * y0 + cnl * x0 + k] << 11;
                fx0y1= rgb_image[old_stride * y1 + cnl * x0 + k] << 11;
                fx1y0= rgb_image[old_stride * y0 + cnl * x1 + k] << 11;
                fx1y1= rgb_image[old_stride * y1 + cnl * x1 + k] << 11;
                z1= (uint)((fx1y0 - fx0y0) * u + fx0y0);
                z2= (uint)((fx1y1 - fx0y1) * u + fx0y1);
                z= (uint)((z2 - z1) * v + z1);
                new_img[new_stride * i + cnl * j + k]= (uint8_t)(z >> 11);
            }
        }
    }
}

这是resize rgb888 CHW格式的图像

/**
 * @brief resize k210 ai image, image have to be CHW
 * 
 * @param old_img 
 * @param new_img 
 * @param old_h 
 * @param old_w 
 * @param new_h 
 * @param new_w 
 */
void resize_ai_img(uint8_t *old_img, uint8_t *new_img, int old_h, int old_w, int new_h, int new_w) {
    if (old_img == nullptr || new_img == nullptr) { return; }

    float rx= (float)old_w / new_w, ry= (float)old_h / new_h;
    float rgx[new_w], rgy[new_h];
    uint x0, y0, x1, y1;
    int fx0y0, fx0y1, fx1y0, fx1y1;
    uint z1, z2, z, stride, new_stride, cnl= 3;
    float u, v;

    for (size_t i= 0; i < new_w; i++) { rgx[i]= (i + 0.5) * rx - 0.5; }
    for (size_t i= 0; i < new_h; i++) { rgy[i]= (i + 0.5) * ry - 0.5; }

    for (size_t k= 0; k < cnl; k++) {
        stride= old_w * old_h * k;
        new_stride= new_w * new_h * k;
        for (size_t i= 0; i < new_h; i++) {
            y0= (uint)rgy[i];
            y1= (y0 + 1) < old_h ? (y0 + 1) : old_h;
            for (size_t j= 0; j < new_w; j++) {
                x0= (uint)rgx[j];
                x1= (x0 + 1) < old_w ? (x0 + 1) : old_w;

                u= rgx[j] - x0;
                v= rgy[i] - y0;

                fx0y0= old_img[stride + old_w * y0 + x0] << 11;
                fx0y1= old_img[stride + old_w * y1 + x0] << 11;
                fx1y0= old_img[stride + old_w * y0 + x1] << 11;
                fx1y1= old_img[stride + old_w * y1 + x1] << 11;

                z1= (uint)((fx1y0 - fx0y0) * u + fx0y0);
                z2= (uint)((fx1y1 - fx0y1) * u + fx0y1);
                z= (uint)((z2 - z1) * v + z1);
                new_img[new_stride + new_w * i + j]= (uint8_t)(z >> 11);
            }
        }
    }
}

效果