图像插值算法及其实现

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 #include #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 = http://kandian.youth.cn/index/*(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