常见图像插值算法

缩放原理

提到缩放,正常人的第一直觉是基于输入图像,增加或减少像素,然后得到目标图像。
但实际工程中,一般采用的是逆向映射的方法。
就是遍历目标图像,找到目标图像的像素对应原图的一个或几个像素。
然后通过原图中的一个或几个像素计算出目标像素值。
这样做的好处是,保证目标图像的每一个像素都有一个确定值,
缩放采用的是逆向映射的方法,遍历输出图像中的每一个像素,找到目标图像对应的原图的位置。
那为什么不用正向映射呢?从输入图像中找到输出图像中的像素不是更方便吗?
如果是从输入图像来推算目标图像,可能出现输出图像的像素点没有灰度值的情况。
举个例子,
在GPS导航系统中,我们通常使用逆向映射的方法来确定车辆当前位置在地图上的具体位置。GPS接收器会接收到一系列的卫星信号,然后根据这些信号确定当前的位置坐标。然后通过地图系统将这些坐标映射到地图上的特定位置,这样它收集到的所有信息都能在地图上反映出来。
假设源图像A大小为m * n,缩放K倍后的目标图像B的大小为M * N。

  1. 缩放比例:k = M / m
  2. 对于目标图像中的每个像素(X,Y),都可以找到原图中的(x,y)
    在这里插入图片描述
  3. 在逆向映射中,(x,y)可能是浮点型的,因为它们不一定正好对应于
    A 中的一个像素位置。因此,通常需要使用插值等方法来计算 (x,y) 周围像素的值,以估算
    (X,Y) 的值。

最常用的插值算法有三种:最近邻插值、双线性插值、立方卷积插值,其中使用立方卷积插值达到的效果是最佳的。

差值算法效果对比

最近邻插值算法

最简单的插值法是最近邻插值法,也叫零阶插值法[2]。。它的原理很简单,即在缩放过程中,对于目标图像中的每个像素,找到在原始图像中距离最近的像素,并将其像素值赋给目标像素。

image.png
如果(i+u, j+v)落在A区,即u<0.5, v<0.5,则将左上角象素的灰度值赋给待求象素,同理,落在B区则赋予右上角的象素灰度值,落在C区则赋予左下角象素的灰度值,落在D区则赋予右下角象素的灰度值。
最邻近元法计算量较小,但可能会造成插值生成的图像灰度上的不连续,在灰度变化的地方可能出现明显的锯齿状。

例程

///scale
typedef struct _VSImage
{
    guchar *pixels;
    int width;
    int height;
    int stride;
}VSImage;

static void vs_scanline_resample_nearest_Y (guchar * dest, guchar * src, int src_width,
    int n, int increment)
{
    int i;
    int j;
    int x;

    for (i = 0; i < n; i++) {
    j = acc >> 16;
    x = acc & 0xffff;
    dest[i] = (x < 32768 || j + 1 >= src_width) ? src[j] : src[j + 1];

    acc += increment;
    }
}

static void vs_image_scale_nearest_Y (const VSImage * dest, const VSImage * src,  guchar * tmpbuf)
{
    int acc;
    int y_increment;
    int x_increment;
    int i;
    int j;

    if (dest->height == 1)
        y_increment = 0;
    else
        y_increment = ((src->height - 1) << 16) / (dest->height - 1);

    if (dest->width == 1)
        x_increment = 0;
    else
        x_increment = ((src->width - 1) << 16) / (dest->width - 1);

    acc = 0;
    for (i = 0; i < dest->height; i++)
    {
        j = acc >> 16;
        
        vs_scanline_resample_nearest_Y (dest->pixels + i * dest->stride,
            src->pixels + j * src->stride, src->width, dest->width, x_increment);
        acc += y_increment;
    }
}

双线性插值算法

双线性插值又叫一阶插值法[3],它要经过三次插值才能获得最终结果,是对最近邻插值法的一种改进,与最邻近差值算法相比,双线性插值算法考虑了目标像素周围的四个最近像素,从而得到更平滑的结果。
在这里插入图片描述

具体步骤如下:

  1. 对于目标图像中的每个像素 (X,Y),计算其在原始图像中的对应位置
    (x,y)。这通常涉及到缩放比例的计算,以及向最近的整数坐标取整。

  2. (x,y) 周围的四个最近像素 (x1,y1),(x2,y2),(x1,y2),(x2,y1),这些像素通常位于原始图像中距离 (x,y) 最近的四个整数坐标。

  3. 计算目标像素 (X,Y) 的像素值,通过对周围四个像素进行加权平均得到。双线性插值的权重基于目标像素与周围四个像素的相对位置关系。
    双线性插值算法是一种常用的图像缩放插值方法,它通过对目标图像中的每个像素进行插值来确定其像素值。与最邻近差值算法相比,双线性插值算法考虑了目标像素周围的四个最近像素,从而得到更平滑的结果。

具体步骤如下:

  1. 对于目标图像中的每个像素 ( (X, Y) ),计算其在原始图像中的对应位置(x, y)。这通常涉及到缩放比例的计算,以及向最近的整数坐标取整。

  2. 确定 (x, y)周围的四个最近像素 ( (x_1, y_1), (x_2, y_1), (x_1, y_2), (x_2, y_2) )。这些像素通常位于原始图像中距离 ( (x, y) ) 最近的四个整数坐标。

  3. 计算目标像素(X, Y) 的像素值,通过对周围四个像素进行加权平均得到。双线性插值的权重基于目标像素与周围四个像素的相对位置关系。

假设目标像素为 (X, Y) ,四个最近像素为 (x_1, y_1), (x_2, y_1), (x_1, y_2), (x_2, y_2) ,其对应的像素值分别为 f(x_1, y_1), f(x_2, y_1), f(x_1, y_2), f(x_2, y_2) 。

则目标像素 (X, Y) 的像素值 ( f(X, Y) ) 可以通过以下公式计算得到:
f ( X , Y ) = ( 1 − α ) ( 1 − β ) f ( x 1 , y 1 ) + α ( 1 − β ) f ( x 2 , y 1 ) + ( 1 − α ) β f ( x 1 , y 2 ) + α β f ( x 2 , y 2 ) f(X, Y) = (1 - \alpha)(1 - \beta) f(x_1, y_1) + \alpha(1 - \beta) f(x_2, y_1) + (1 - \alpha)\beta f(x_1, y_2) + \alpha\beta f(x_2, y_2) f(X,Y)=(1α)(1β)f(x1,y1)+α(1β)f(x2,y1)+(1α)βf(x1,y2)+αβf(x2,y2)

其中,alpha和beta 分别表示目标像素 ( (X, Y) ) 在 ( x ) 和 ( y ) 方向上与最近像素 ( (x_1, y_1) ) 之间的距离比例。这些比例通常通过插值计算得到。

双线性插值算法通过考虑像素周围的局部信息,可以得到更平滑、更准确的图像缩放结果,相较于最邻近差值算法,在视觉上更加自然。因此,在图像处理和计算机视觉领域,双线性插值算法是一种常用的图像缩放插值方法。

例程

///scale
typedef struct _VSImage
{
    guchar *pixels;
    int width;
    int height;
    int stride;
} VSImage;

static void vs_scanline_resample_bilinear_Y(guchar *dest, guchar *src, int src_width,
                                            int n, int increment)
{
    int i;
    int j;
    int x;

    for (i = 0; i < n; i++)
    {
        j = acc >> 16;
        x = acc & 0xffff;
        
        // 计算四个最近像素的值
        int p00 = src[j];
        int p10 = (j + 1 < src_width) ? src[j + 1] : p00;
        int p01 = (j + src->stride < src_width) ? src[j + src->stride] : p00;
        int p11 = (j + 1 < src_width && j + src->stride < src_width) ? src[j + 1 + src->stride] : p00;

        // 双线性插值计算
        int value = (p00 * (65536 - x) * (65536 - y) + p10 * x * (65536 - y) +
                     p01 * y * (65536 - x) + p11 * x * y) >> 32;
        
        dest[i] = value;
        acc += increment;
    }
}

static void vs_image_scale_bilinear_Y(const VSImage *dest, const VSImage *src, guchar *tmpbuf)
{
    int acc;
    int y_increment;
    int x_increment;
    int i;
    int j;

    if (dest->height == 1)
        y_increment = 0;
    else
        y_increment = ((src->height - 1) << 16) / (dest->height - 1);

    if (dest->width == 1)
        x_increment = 0;
    else
        x_increment = ((src->width - 1) << 16) / (dest->width - 1);

    acc = 0;
    for (i = 0; i < dest->height; i++)
    {
        j = acc >> 16;
        
        vs_scanline_resample_bilinear_Y(dest->pixels + i * dest->stride,
                                         src->pixels + j * src->stride, src->width, dest->width, x_increment);
        acc += y_increment;
    }
}

立方卷积插值

双三次插值又称立方卷积插值。三次卷积插值是一种更加复杂的插值方式。该算法利用待采样点周围16个点的灰度值作三次插值,不仅考虑到4 个直接相邻点的灰度影响,而且考虑到各邻点间灰度值变化率的影响。三次运算可以得到更接近高分辨率图像的放大效果,但也导致了运算量的急剧增加。

具体步骤如下,

  1. 选取(x, y)附近的16个点作为参考像素
    在这里插入图片描述
    如上图所示,当我们求出结果图某个像素对应于原图的像素P00时(映射点),其他的15个像素位置就知道了。
    所以关键就在于找到P00。
    找到P00很简单,方法如下
    在这里插入图片描述
    首先计算它映射到原图中的坐标(i + v, j + u)
    然后卷积计算时,p00点对应(i, j)坐标

  2. 最后利用BiCubic基函数求出16个像素点的权重,(x,y)的值就等于16个像素点的加权叠加。
    不要被下面复杂的公式吓到,它只是用来计算这16个像素点的权重的。
    image.png
    我们要做的就是求出BiCubic函数中的参数x,从而获得上面所说的16个像素所对应的权重W(x)。BiCubic基函数是一维的,而像素是二维的,所以我们将像素点的行与列分开计算。
    最终计算公式如下
    在这里插入图片描述

例程

///scale_cubic
typedef struct _VSImage
{
    guchar *pixels;
    int width;
    int height;
    int stride;
} VSImage;

static int cubic_convolution(int p[4], double x)
{
    double x2, x3;
    x2 = x * x;
    x3 = x2 * x;
    return (int)(0.5 * (p[1] + (x - 1) * (p[2] - p[0] + (x - 2) * (p[0] - 2 * p[1] + p[2] + (x - 3) * (2 * p[1] - p[0] - p[2] + (x - 4) * (p[0] - 3 * p[1] + 3 * p[2] - p[3]))))));
}

static void vs_scanline_resample_cubic_Y(guchar *dest, guchar *src, int src_width,
                                          int n, double increment)
{
    int i, j;
    double x;
    int p[4];

    for (i = 0; i < n; i++)
    {
        j = acc >> 16;
        x = acc & 0xffff;

        // 计算周围四个像素的值
        p[0] = (j - 1 >= 0) ? src[j - 1] : src[j];
        p[1] = src[j];
        p[2] = (j + 1 < src_width) ? src[j + 1] : src[j];
        p[3] = (j + 2 < src_width) ? src[j + 2] : src[j];

        // 计算立方卷积插值
        dest[i] = (guchar)cubic_convolution(p, x / 65536.0);

        acc += increment;
    }
}

static void vs_image_scale_cubic_Y(const VSImage *dest, const VSImage *src, guchar *tmpbuf)
{
    int acc;
    double y_increment;
    double x_increment;
    int i, j;

    if (dest->height == 1)
        y_increment = 0;
    else
        y_increment = ((src->height - 1) << 16) / (double)(dest->height - 1);

    if (dest->width == 1)
        x_increment = 0;
    else
        x_increment = ((src->width - 1) << 16) / (double)(dest->width - 1);

    acc = 0;
    for (i = 0; i < dest->height; i++)
    {
        j = acc >> 16;

        vs_scanline_resample_cubic_Y(dest->pixels + i * dest->stride,
                                      src->pixels + j * src->stride, src->width, dest->width, x_increment);
        acc += (int)y_increment;
    }
}
  • 8
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在MATLAB中,图像插值是一种常见的处理图像数据的技术,用于在图像的像素之间插入新的数据点,以提高图像的分辨率或平滑图像。MATLAB提供了多种图像插值方法,包括: 1. **nearest neighbor(最近邻插值)**:最简单的插值方法,每个新点的值取其最近的像素值,适用于简单放大或缩小图像。 2. **bilinear interpolation(双线性插值)**:基于四个相邻像素进行插值,适用于小范围的放大,保持了图像的连续性。 3. **bicubic interpolation(双三次样条插值)**:更复杂的方法,使用九个相邻像素的权重进行插值,效果比双线性更平滑,适用于更大的放大比例。 4. **imresize() 函数**:MATLAB内建函数,提供了一种灵活的方式来调整图像尺寸,可以根据不同插值模式如'nearest', 'linear', 'cubic'等选择插值方法。 5. **imageinterpolation toolbox(图像插值工具箱)**:MATLAB专用工具箱,提供了更多的高级插值算法,如sinc插值、lanczos插值等,适用于专业图像处理需求。 6. **Nearest Neighbour Interpolation(最近邻内插)**:这种插值方法不考虑像素间的空间关系,仅按位置选取最近的像素。 要使用这些方法,通常你需要将原始图像转换为插值后的图像,例如: ```matlab % 加载图像 img = imread('your_image.jpg'); % 使用bicubic插值放大图像 resized_img = imresize(img, [new_size new_size], 'bicubic'); % 或者直接指定插值模式 resized_img = imresize(img, [new_size new_size], 'cubic'); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值