图像插值算法及其实现

sensor、codec、display device都是基于pixel的,高分辨率图像能呈现更多的detail,由于sensor制造和chip的限制,我们需要用到图像插值(scaler/resize)技术,这种方法代价小,使用方便 。同时,该技术还可以放大用户希望看到的感兴趣区域 。图像缩放算法往往基于插值实现,常见的图像插值算法包括最近邻插值(Nearest-neighbor)、双线性插值(Bilinear)、双立方插值(bicubic)、lanczos插值、方向插值(Edge-directed interpolation)、example-based插值、深度学习等算法 。
插值缩放的原理是基于目标分辨率中的点,将其按照缩放关系对应到源图像中,寻找源图像中的点(不一定是整像素点),然后通过源图像中的相关点插值得到目标点 。本篇文章,我们介绍Nearest-neighbor和Bilinear插值的原理及C实现 。
插值算法原理如下:

图像插值算法及其实现

文章插图
 
1. Nearest-neighbor最近邻插值,是指将目标图像中的点,对应到源图像中后,找到最相邻的整数点,作为插值后的输出 。如下图所示,P为目标图像对应到源图像中的点,Q11、Q12、Q21、Q22是P点周围4个整数点,Q12与P离的最近,因此P点的值等于Q12
图像插值算法及其实现

文章插图
 
的值 。这里写图片描述由于图像中像素具有邻域相关性,因此,用这种拷贝的方法会产生明显的锯齿 。
2. Bilinear双线性插值使用周围4个点插值得到输出,双线性插值,是指在xy方法上,都是基于线性距离来插值的 。
如图1,目标图像中的一点对应到源图像中点P(x,y),我们先在x方向插值:
图像插值算法及其实现

文章插图
 
然后,进行y方向插值:
图像插值算法及其实现

文章插图
 
可以验证,先进行y方向插值再进行x方向插值,结果也是一样的 。值得一提的是,双线性插值在单个方向上是线性的,但对整幅图像来说是非线性的 。
3. C实现使用VS2010,工程包含三个文件,如下:
图像插值算法及其实现

文章插图
 
main.cpp
#include <string.h>#include <IOStream>#include "resize.h"int main(){ const char *input_file = "D:\simuTest\teststream\00_YUV_data\01_DIT_title\data.yuv";//absolute path const char *output_file = "D:\simuTest\teststream\00_YUV_data\01_DIT_title\data_out2.yuv"; //absolute pathint src_width = 720; int src_height = 480; int dst_width = 1920; int dst_height = 1080; int resize_type = 1;//0:nearest, 1:bilinear resize(input_file, src_width, src_height, output_file, dst_width, dst_height, resize_type); return 0;}resize.cpp
#include "resize.h"int clip3(int data, int min, int max){ return (data > max) ? max : ((data < min) ? min : data); if(data > max)return max; else if(data > min)return data; elsereturn min;}//bilinear takes 4 pixels (2×2) into account/** 函数名: bilinearHorScaler* 说明: 水平方向双线性插值* 参数:*/void bilinearHorScaler(int *src_image, int *dst_image, int src_width, int src_height, int dst_width, int dst_height){ double resizeX = (double)dst_width / src_width; for(int ver = 0; ver < dst_height; ++ver){for(int hor = 0; hor < dst_width; ++hor){double srcCoorX = hor / resizeX;double weight1 = srcCoorX - (double)((int)srcCoorX);double weight2 = (double)((int)(srcCoorX + 1)) - srcCoorX;double dstValue = https://www.isolves.com/it/cxkf/sf/2020-11-12/*(src_image + src_width * ver + clip3((int)srcCoorX, 0, src_width - 1)) * weight2 + *(src_image + src_width * ver + clip3((int)(srcCoorX + 1), 0, src_width - 1)) * weight1;*(dst_image + dst_width * ver + hor) = clip3((uint8)dstValue, 0, 255);} }}/** 函数名: bilinearVerScaler* 说明: 垂直方向双线性插值* 参数:*/void bilinearVerScaler(int *src_image, int *dst_image, int src_width, int src_height, int dst_width, int dst_height){ double resizeY = (double)dst_height / src_height; for(int ver = 0; ver < dst_height; ++ver){for(int hor = 0; hor < dst_width; ++hor){double srcCoorY = ver / resizeY;double weight1 = srcCoorY - (double)((int)srcCoorY);double weight2 = (double)((int)(srcCoorY + 1)) - srcCoorY;double dstValue = *(src_image + src_width * clip3((int)srcCoorY, 0, src_height - 1) + hor) * weight2 + *(src_image + src_width * clip3((int)(srcCoorY + 1), 0, src_height - 1) + hor) * weight1;*(dst_image + dst_width * ver + hor) = clip3((uint8)dstValue, 0, 255);} }}/** 函数名: yuv420p_NearestScaler* 说明: 最近邻插值* 参数:*/void nearestScaler(int *src_image, int *dst_image, int src_width, int src_height, int dst_width, int dst_height){ double resizeX = (double)dst_width /src_width;//水平缩放系数 double resizeY = (double)dst_height / src_height;//垂直缩放系数 int srcX = 0; int srcY = 0; for(int ver = 0; ver


推荐阅读