最邻近插值算法和双线性插值手工实现

 通过手动实现最邻近插值算法和双线性插值。

 

最邻近插值:

优点: 计算量很小,算法简单,速度快。

缺点: 仅使用离待测采样点最近的像素的灰度值作为该采样点的灰度值,而没有考虑其他相邻像素点的影响,因此采样后的灰度值有明显的不连续性,图像质量损失较大,会产生明显的马赛克和锯齿现象。

import cv2
import numpy as np


def nearest_interpolation(src_img, out_dim):
    dst_width, dst_height = out_dim[0], out_dim[1]  # 目标图像的宽与高
    if len(src_img.shape) > 2:  # 说明不是灰度图
        src_width, src_height, channels = src_img.shape
        # 创建新图,是一个全0矩阵
        dst_img = np.zeros((dst_height, dst_width, channels), dtype=np.uint8)
        scale_x, scale_y = float(src_width) / dst_width, float(src_height) / dst_height
        for dsth in range(dst_height):
            for dstw in range(dst_width):
                # 几何中心对称
                src_x = (dstw + 0.5) * scale_x - 0.5
                src_y = (dsth + 0.5) * scale_y - 0.5
                # 防止放大后在原图后越界,做越界处理,比如原图为512*512,取到520时不存在,就取512
                src_x0 = int(np.floor(src_y))
                src_x1 = min(src_x0 + 1, src_height - 1)
                src_y0 = int(np.floor(src_x))
                src_y1 = min(src_y0 + 1, src_width - 1)
                dst_img[dsth, dstw] = img[src_x1, src_y1]

        return dst_img  # 返回目标图像

    else:  # 是灰度图
        src_width, src_height = src_img.shape  # 原图大小
        # 创建新图的0矩阵
        dst_img = np.zeros((dst_height, dst_width), dtype=np.uint8)
        scale_x, scale_y = float(src_width) / dst_width, float(src_height) / dst_height
        for dsth in range(dst_height):
            for dstw in range(dst_width):
                src_x = (dstw + 0.5) * scale_x - 0.5
                src_y = (dsth + 0.5) * scale_y - 0.5
                # 防止放大后在原图后越界,做越界处理,比如原图为512*512,取到520时不存在,就取512
                src_x0 = int(np.floor(src_y))
                src_x1 = min(src_x0 + 1, src_height - 1)
                src_y0 = int(np.floor(src_x))
                src_y1 = min(src_y0 + 1, src_width - 1)
                dst_img[dsth, dstw] = img[src_x1, src_y1]
        return dst_img


if __name__ == '__main__':
    img = cv2.imread("../data/lenna.png")
    print("origina_image size:", img.shape)
    dst_img = nearest_interpolation(img, (1000, 1000))
    print("new_image size:", dst_img.shape)
    cv2.imshow("dst_img", dst_img)
    cv2.imshow("original_img", img)
    cv2.waitKey(0)

双线性插值算法:

优点:双线性插值效果比最近邻插值效果好,缩放后的图像质量高。 因为考虑了待采样点周围四个直接邻点对该采样点的相关性影响,基本克服了最近邻插值不连续的缺点。

缺点:计算量稍大,算法更复杂一些,运算时间长。 仅考虑待测样点周围四个点灰度值的影响,而为考虑各邻点间灰度值变化率的影响,因此具有低通滤波器的性质,导致缩放后图像的高频分量受到损失,图像边缘产生一定程度的模糊。 用此方法缩放后的图像与输入图像相比,仍然存在由于插值函数设计考虑不周而产生的图像质量受损的问题。

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]
    print("src_h, src_w = ", src_h, src_w)
    print("dst_h, dst_w = ", dst_h, dst_w)

    if src_h == dst_h and src_w == dst_w:  # 若目标图和原图一样大小,直接复制即可
        return img.copy()
    # 创建目标图像,三通道
    dst_img = np.zeros((dst_h, dst_w, 3), dtype=np.uint8)
    # 获取水平、垂直方向的缩放比例
    scale_x, scale_y = float(src_w) / dst_w, float(src_h) / dst_h
    for i in range(3): #3个通道
        for dst_y in range(dst_h):
            for dst_x in range(dst_w):
                # 计算源图像坐标
                src_x = (dst_x + 0.5) * scale_x - 0.5
                src_y = (dst_y + 0.5) * scale_y - 0.5
                src_x0 = int(np.floor(src_x))
                src_x1 = min(src_x0 + 1, src_w - 1)  # 防止坐标越界,比如原图为512*512,取到520时不存在,就取512
                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__':
    img = cv2.imread('../data/lenna.png')
    dst = bilinear_interpolation(img, (1000, 1000))
    cv2.imshow("original img", img)
    cv2.imshow('bilinear interp', dst)
    cv2.waitKey()

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值