前言

本文先讲述图像的上采样,与之相对的下采样因为涉及到滤波的知识,会在图像滤波器结束之后继续更新。上采样的底层原理涉及到一些数学计算,如果是正常学习视觉知识,建议跟着文章的步骤过一遍计算思路。如果是辅助验证需要代码,仅需到文章最后自取即可。


一、名词介绍

在开始之前,我们先补充介绍几个视觉中常见的名词。
图像分辨率(Image Resolution):单位英寸内的像素点数。单位为PPI(Pixels Per Inch),通常叫做像素每英寸。
幅值(Amplitude):“某个物理量偏离‘基准值’的最大程度”。举个例子:假设我们现在以黑色(255)设为基准值,某个像素点的像素值是200,那么他的幅值为黑色基准值与像素点之间的差,即200。
上采样(Upsampling):通过增加图像像素数量,提高图像分辨率。比如一张图像是512×512,现在增加到1024×1024,即为上采样。
下采样(Downsampling):通过减少图像像素数量,降低图像分辨率的过程。比如一张图像是1024×1024,现在减少到512×512,即为下采样。
在这里插入图片描述

二、上采样

1.底层逻辑定义

上采样本质是 “通过算法增加图像的像素数量,将低分辨率图像(像素少、细节粗)转换为高分辨率图像(像素多、细节更连贯)” 的过程。需要注意的是,上采样的核心不是 “简单放大画面”,而是“为新增的空白像素填充‘符合原图像特征的合理信息”。简单来说,就是对现有图像进行内插值处理。整体步骤我们可以大致分成三步来进行。

(1)定尺寸

先明确 “要把低分辨率图像放大到多少像素”。这个要根据实际情况来定,但是有几点需要注意。
I:优先采用整数倍放大(如2倍、4倍),避免非整数倍导致像素位置错位,否则后续插值算法容易存在计算误差与细节失真;
II:结合原图像细节密度定尺寸:若原图细节少(如纯色图标),可适度放大;若原图模糊 / 噪声多,避免过度放大(如>4倍),防止生成假细节加重失真。
III:彩色图需确保 R、G、B 三通道同步放大,防止通道尺寸不一致引发颜色错位,破坏色彩准确性。
我们还是以经典的lenna图像为例:原图像是512×512像素,要上采样到1024×1024像素。
在这里插入图片描述

(2)找空位

我们定好目标尺寸了,现在我们需要的做的是,在一张512×512的找到我要填充的像素空格,使其扩充到1024×1024。
给每个 “像素空格” 算 “原图像对应位置”。对目标图像中每一个像素,用坐标公式算出它在原512×512图像中的 “理论对应坐标”,公式为:
在这里插入图片描述

其中srcX(Y)为原图像X(Y)坐标,dstX(Y)为目标图像X(Y)坐标,srcWidth为(原图像宽-1),dstWidth为(目标图像宽-1),srcHeight为(原图像高-1),dstHeight为(目标图像高-1)。
其中需要注意三点:
I:(原图像宽度 - 1)/(目标图像宽度 - 1) 是坐标映射的比例因子。
II:(原图像宽度 - 1)/(目标图像宽度 - 1)中的-1是因为图像像素坐标从 “0” 开始计数,而非从 “1” 开始。
III:为了让原始像素在新网格中保持对称位置,确保插值计算的准确性和结果的视觉一致性,避免图像偏移或失真。我们需要进行像素点中心化。那么为什么要做中心化处理,我们看图理解:
在这里插入图片描述
将一个3×3的图像进行上采样处理,得到一张6×6的图像。对比上采样时像素点中心化与不中心化的情况。中间做中心化的图像原始像素位于新网格对应区域中心,能保证插值计算时原始像素与周围新像素距离对称;右侧是不做中心化的上采样,原始像素处于新网格对应区域的非中心位置(如 (0,0) 在左上角),会导致原始像素与周围新像素距离不均,使上采样后图像出现偏移或视觉失真。那么怎么做像素点中心化处理?这边需要一个小的计算,如下:
在这里插入图片描述
由此我们可以得知,只需要在等式两边加上0.5,即可使变换前后图像对齐。

(3)填像素

上面的定尺寸和找空位是大部分上采样算法的通解,而不同算法的主要区别在于填像素这一步。在这边我们介绍两种常见的算法:最邻近插值、双线性插值。
I:最邻近插值:
在上面的基础上,最邻近插值就是给新像素 “选最近的原像素”。在刚才算的理论位置有一个问题,就是该位置可能是小数(比如算出来是 75.3、40.6),这时候不用复杂计算,直接把小数四舍五入取整 —— 比如 75.3≈75,40.6≈41,就选原图里 (75, 41) 这个像素当 “邻居”。“直接复制邻居的颜色”。如果原图 (75, 41) 是浅红色,就把1024×1024大图里 (150, 80) 这个新像素也填成浅红色,这一步就是 “填像素” 的核心,没有任何额外计算,纯复制。
在这里插入图片描述
II:双线性插值:
在讲解双线性插值之前,我们先来看看单线性插值。如图所示,在这样的一个绿色三角形中,我们已知x0,y0和x1,y1的位置,对于三角形中,任意x,y,我们都能根据相似三角形的定理推导出最后y的表达式。
在这里插入图片描述
再来看双线性插值,所谓双线性,即进行两次单线性。双线性插值计算过程如下:
在这里插入图片描述
这里的f(x,y)即为该像素点的像素值。

三、代码实现

1.最邻近插值

import cv2
import numpy as np

def nearest_interpolation(src):
    height, width, channels = src.shape
    dst_img = np.zeros((1024, 1024, channels), np.uint8)

    # 计算缩放比例
    scale_h = height / 1024
    scale_w = width / 1024

    # 遍历目标图像
    for i in range(1024):
        for j in range(1024):
            x = (i + 0.5) * scale_h - 0.5
            y = (j + 0.5) * scale_w - 0.5

            # 注意,在python中,int(),是转为整型,向下取整,为了达到四舍五入的效果,我们需要在后面加上0.5。
            x = int(i * scale_h + 0.5)
            y = int(j * scale_w + 0.5)

            # 防止越界
            x = min(max(x, 0), height - 1)
            y = min(max(y, 0), width - 1)

            dst_img[i, j] = src[x, y]
    return dst_img

src = cv2.imread("lenna.png")
dst = nearest_interpolation(src)
cv2.imshow("nearest_interpolation", dst)
cv2.imshow("image", src)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.双线性插值

import numpy as np
import cv2

def bilinear_interpolation(img, out_dim):
    src_h, src_w, channel = img.shape
    dst_h, dst_w = out_dim[1], out_dim[0]
    dst_img = np.zeros((dst_h, dst_w, 3), dtype=np.uint8)

    scale_w, scale_h = float(src_w) / dst_w, float(src_h) / dst_h

    for i in range(channel):
        for dst_y in range(dst_h):
            for dst_x in range(dst_w):
                src_x = (dst_x + 0.5) * scale_w - 0.5
                src_y = (dst_y + 0.5) * scale_h - 0.5

                src_x0 = int(np.floor(src_x))
                src_x1 = min(src_x0 + 1, src_w - 1)
                src_y0 = int(np.floor(src_y))
                src_y1 = min(src_y0 + 1, src_h - 1)

                # 按照公式来,双线性插值计算
                temp0 = (src_x1 - src_x) * img[src_y0, src_x0, i] + (src_x - src_x0) * img[src_y0, src_x1, i]
                temp1 = (src_x1 - src_x) * img[src_y1, src_x0, i] + (src_x - src_x0) * img[src_y1, src_x1, i]
                dst_img[dst_y, dst_x, i] = int((src_y1 - src_y) * temp0 + (src_y - src_y0) * temp1)

    return dst_img

if __name__ == '__main__':
    src = cv2.imread('lenna.png')
    dst = bilinear_interpolation(src, (1024, 1024))
    cv2.imshow('bilinear_interpolation', dst)
    cv2.imshow("image", src)
    cv2.waitKey()
    cv2.destroyAllWindows()


总结

上采样在工程中应用广泛,核心是将低分辨率数据提升至更高分辨率以满足需求:比如计算机视觉领域用于图像显示适配、目标检测预处理及医学影像细节增强;多媒体与游戏领域实现画质修复、实时渲染优化;工业检测中助力微小缺陷识别。上述为比较常见的两种上采样算法,除此以外还有很多类似的算法,例如双三次插值、样条插值、Lanczos插值等等,本文就不一一介绍了。
(总目录跳转链接)

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐