Resize:最近邻插值、双线性插值、双三次插值

本文详细介绍了OpenCVresize函数中三种插值方法:最近邻插值、双线性插值(默认)和双三次插值。作者通过实例演示了这些方法的工作原理、代码实现以及它们的特点,包括图像质量、速度和适用场景。
摘要由CSDN通过智能技术生成

Opencv resize函数

官方文档:Opencv resize()

void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR );
参数含义: 
InputArray src     -原图像 
OutputArray dst    -输出图像 
Size dsize         -目标图像的大小 
double fx=0        -在x轴上的缩放比例 
double fy=0        -在y轴上的缩放比例 
int interpolation  -插值方式,有以下5种方式 

INTER_NEAREST      -最近邻插值 
INTER_LINEAR       -双线性插值 (默认使用) 
INTER_AREA         -区域插值。 
INTER_CUBIC        -三次样条插值,也叫立方插值(超过4*4像素邻域内的双三次插值)
INTER_LANCZOS4     -Lanczos插值(超过8*8像素邻域的Lanczos插值)

当不输入fx和fy时,函数会自动计算

fx = float(src.shape[1] / dsize[1])
fy = float(src.shape[0] / dsize[0])

python中cv2.resize默认的是INTER_LINEAR官方建议:如果是缩小图片,使用INTER_AREA插值算法看起来是最好的,如果是放大图片,可以选择INTER_CUBIC(慢)或INTER_LINEAR(快但效果还不错)。

官方原文:To shrink an image, it will generally look best with INTER_AREA interpolation, whereas to enlarge an image, it will generally look best with INTER_CUBIC (slow) or INTER_LINEAR (faster but still looks OK).

1. 最近邻插值(INTER_NEAREST)

1.1 原理

最近邻插值,也称为零阶插值。计算原理为,目标图像位置直接采用与它最邻近位置的原始图像的像素点为其赋值

目标图像位置(dst_x, dst_y)最邻近的原始图像位置(src_x, src_y)的计算:src_x = int(dst_x / scale_x)src_y = int(dst_y / scale_y),其中scale_xscale_y分别表示在图像宽度方向和高度方向的缩放比例。

1.2 代码实例

使用一个灰度图实例进行放大缩小

import numpy as np
import cv2

# 生成3*3的灰度图
img = np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=np.uint8)

# 放大一倍
resized_up = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_NEAREST)

# 缩小一倍
resized_down = cv2.resize(img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_NEAREST)

# 缩小一倍再放大一倍
resized_down_up = cv2.resize(resized_down, None, fx=2, fy=2, interpolation=cv2.INTER_NEAREST)

print(img, resized_up, resized_down, resized_down_up)
(array([[1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]], dtype=uint8),
 array([[1, 1, 2, 2, 3, 3],
        [1, 1, 2, 2, 3, 3],
        [4, 4, 5, 5, 6, 6],
        [4, 4, 5, 5, 6, 6],
        [7, 7, 8, 8, 9, 9],
        [7, 7, 8, 8, 9, 9]], dtype=uint8),
 array([[1, 3],
        [7, 9]], dtype=uint8)),
array([[1, 1, 3, 3],
       [1, 1, 3, 3],
       [7, 7, 9, 9],
       [7, 7, 9, 9]], dtype=uint8)

由上可知:

  • 当dsize不指定的时候,则由fx和fy计算后四舍五入得到
  • 对同一张图缩小再放大,与原图不会保持一致
1.3 简单的代码复现
  • 官方函数
import cv2

# 读取图像
img = cv2.imread('F:/Datas/kaggle/archive/test/apple/apple.jpg')

# 缩放尺寸
dim = (int(img.shape[1] * 2), int(img.shape[0] * 2))

# 使用最近邻插值缩放图像
resized = cv2.resize(img, dim, fx=2, fy=2, interpolation = cv2.INTER_NEAREST)

# 显示缩放后的图像
cv2.imshow("Original image", img)
cv2.imshow("Resized image", resized)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 将图片写入本地
cv2.imwrite("F:/Datas/kaggle/archive/test/apple/applex2.jpg", resized)
  • 简单的复现
def resizeNearestRGB(src, dsize, fx, fy):
    cols, rows = dsize
    dst = np.zeros((rows, cols, 3), dtype=np.uint8)

    for i in range(rows):
        for j in range(cols):
            x = int(j / fx)
            y = int(i / fy)
            dst[i, j][0] = src[y, x][0]
            dst[i, j][1] = src[y, x][1]
            dst[i, j][2] = src[y, x][2]

    return dst

img = cv2.imread('F:/Datas/kaggle/archive/test/apple/apple.jpg')

dim = (int(img.shape[1] * 2), int(img.shape[0] * 2))
new_image = resizeNearestRGB(img, dim, 2, 2)

cv2.imshow("Original image", img)
cv2.imshow("Resized image", new_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 比较两个矩阵是否相等
if np.array_equal(resized, new_image):
    print("两个矩阵相等")
else:
    print("两个矩阵不相等")
    
OUT: 两个矩阵相等

在这里插入图片描述

1.4 特点

由于是以最近的点作为新的插入点,因此边缘不会出现渐变过渡区域,这也导致缩放后的图像容易出现锯齿的现象

在这里插入图片描述

2. 双线性插值(INTER_LINEAR)(默认值)

2.1 原理

双线性插值,又称双线性内插。其核心思想是在x和y两个方向分别进行线性插值。

1) 将目标图像的位置(dst_x, dst_y)映射到原图P(src_x, src_y)中,但这时取得的是float格式的结果;

src_x = float(dst_x / scale_x)
src_y = float(dst_y / scale_y)

2)得到P点在原图中最邻近的4个点Q11Q12Q21Q22
在这里插入图片描述

3)首先在x方向进行两次线性插值得到R1R2两个点的像素值f(R1)f(R2),然后再在y方向一次线性插值得到最终点P的像素值f(P),将该值赋值给目标图像(dst_x, dst_y)注意此处如果先在y方向插值,再在x方向插值,其结果是一样的

在这里插入图片描述

在这里插入图片描述

2.2 简单的代码复现
  • 官方代码
import cv2

# 读取图像
img = cv2.imread('F:/Datas/kaggle/archive/test/apple/apple.jpg')

# 缩放尺寸
dim = (int(img.shape[1] * 2), int(img.shape[0] * 2))

# 使用最近邻插值缩放图像
resized = cv2.resize(img, dim, fx=2, fy=2, interpolation = cv2.INTER_LINEAR)

# 显示缩放后的图像
cv2.imshow("Original image", img)
cv2.imshow("Resized image", resized)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 将图片写入本地
cv2.imwrite("F:/Datas/kaggle/archive/test/apple/applex2.jpg", resized)
  • 简单的复现
def resizeNearestRGB(src, dsize, fx, fy):
    cols, rows = dsize
    dst = np.zeros((rows, cols, 3), dtype=np.uint8)

    for i in range(rows):
        for j in range(cols):
            x = j / fx
            y = i / fy
            x1, y1 = int(x), int(y)
            x2 = min(x1 + 1, src.shape[1] - 1)
            y2 = min(y1 + 1, src.shape[0] - 1)
            Q11, Q21 = src[y1, x1], src[y1, x2]
            Q12, Q22 = src[y2, x1], src[y2, x2]
            if x2 == x1:
                R1 = Q11
                R2 = Q12
            else:
                R1 = Q11 * (x2 - x)/(x2 - x1) + Q21 * (x - x1)/(x2 - x1)
                R2 = Q12 * (x2 - x)/(x2 - x1) + Q22 * (x - x1)/(x2 - x1)
            if y2 == y1:
                P = R1
            else:
                P = R1 * (y2 - y)/(y2 - y1) + R2 * (y - y1)/(y2 - y1)
            dst[i, j] = P

    return dst


image = cv2.imread('F:/Datas/kaggle/archive/test/apple/apple.jpg')

new_size = (int(image.shape[1] * 2), int(image.shape[0] * 2))
new_image = resizeNearestRGB(image, new_size, 2, 2)

cv2.imshow('new image', new_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

自己复现的代码得到的像素值和opencv中内部实现存在一点偏差,可能opencv内部有进行优化
在这里插入图片描述

双线性内插值算法放大后的图像质量较高,不会出现像素值不连续的情况,然而此算法具有低通滤波器的性能,使高频分量受损,所以可能会使图像轮廓在一定程序上变得模糊。

3. 双三次插值(INTER_CUBIC)

在这里插入图片描述

双立方插值是双线性插值的扩展,又称双三次插值,使用相邻的16(4x4)个像素点的加权之和进行插值,每个像素的权重由基于距离的函数取得。

双立方插值算法比双线性插值能更好地保留细节、增加锐度和清晰度,但是,它可能会导致波纹。

在这里插入图片描述
在这里插入图片描述

4. 三种方法的直观对比

三种方法的对比:25(5x5)个拼凑在一起的单位方块,颜色表示函数值,黑点是指定数据被插值的位置。双线性插值得到的图像在正方形边界处会有像素值突变现象。
在这里插入图片描述

参考

图像处理中常见的几种插值方法:最近邻插值、双线性插值、双三次插值(附Pytorch测试代码)

维基百科

  • 20
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值