图像缩放的双线性插值实现方式
1、双线性插值概念
双线性插值是一种用于在二维网格上进行插值的方法,适用于图像处理、计算机图形学等领域。它通过利用四个邻近点的已知值,估算出任意点的值。双线性插值在两个方向(通常是水平和垂直)上分别进行线性插值,因此得名“双线性”。双线性插值是一种常用的图像缩放方法,它通过在四个最近邻像素之间进行线性插值来计算新的像素值。以下是双线性插值的详细步骤和公式。
双线性插值的步骤
假设我们有一个源图像 I(x, y),目标是将其缩放到一个新的尺寸 (new_width, new_height)。对于目标图像中的每一个像素 (xx, yy),我们需要找到其在源图像中的对应位置,并使用双线性插值计算该像素的值。
-
确定源图像中的坐标:
- 计算目标图像中每个像素
(xx, yy)对应的源图像坐标(x, y)。 - 使用缩放比例
xRatio = (src_width - 1) / (new_width - 1)和yRatio = (src_height - 1) / (new_height - 1)来计算源图像坐标。 x = floor(xx * xRatio)和y = floor(yy * yRatio)得到最接近的左上角像素坐标。x_l和y_l分别是x和y的整数部分,x_h = min(x_l + 1, src_width - 1)和y_h = min(y_l + 1, src_height - 1)是右下角的像素坐标。
- 计算目标图像中每个像素
-
计算权重:
- 计算小数部分
dx = xx * xRatio - x_l和dy = yy * yRatio - y_l。 - 这些小数部分将用于线性插值。
- 计算小数部分
-
双线性插值公式:
- 使用四个最近邻像素的值
I(x_l, y_l)、I(x_h, y_l)、I(x_l, y_h)和I(x_h, y_h)进行插值。 - 首先在水平方向上进行线性插值:
a = I ( x l , y l ) ⋅ ( 1 − d x ) + I ( x h , y l ) ⋅ d x a = I(x_l, y_l) \cdot (1 - dx) + I(x_h, y_l) \cdot dx a=I(xl,yl)⋅(1−dx)+I(xh,yl)⋅dx b = I ( x l , y h ) ⋅ ( 1 − d x ) + I ( x h , y h ) ⋅ d x b = I(x_l, y_h) \cdot (1 - dx) + I(x_h, y_h) \cdot dx b=I(xl,yh)⋅(1−dx)+I(xh,yh)⋅dx - 然后在垂直方向上进行线性插值:
I ′ ( x x , y y ) = a ⋅ ( 1 − d y ) + b ⋅ d y I'(xx, yy) = a \cdot (1 - dy) + b \cdot dy I′(xx,yy)=a⋅(1−dy)+b⋅dy
- 使用四个最近邻像素的值
2、双线性插值实现代码(NumPy,CV2)
2.1 Python代码
提供了3种实现方式:for-loop、NumPy广播机制、CV2库函数:
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
import time
import cv2#方式1:for-loop
def bilinear_resize(img, new_shape):img = np.array(img)height, width, depth = img.shapenew_height, new_width = new_shaperesult = np.zeros((new_height, new_width, depth))x_ratio = float(width - 1) / new_widthy_ratio = float(height - 1) / new_heightfor i in range(new_height):for j in range(new_width):x_l, y_l = int(j * x_ratio), int(i * y_ratio)x_h, y_h = min(x_l + 1, width - 1), min(y_l + 1, height - 1)x_weight = (j * x_ratio) - x_ly_weight = (i * y_ratio) - y_la = img[y_l, x_l] * (1 - x_weight) + img[y_l, x_h] * x_weightb = img[y_h, x_l] * (1 - x_weight) + img[y_h, x_h] * x_weightresult[i, j] = a * (1 - y_weight) + b * y_weightreturn Image.fromarray(np.uint8(result))#方式2:NumPy广播机制
def bilinear_resize_numpy(img, new_shape):img = np.array(img)height, width, depth = img.shapenew_height, new_width = new_shape# 计算缩放比例x_ratio = float(width - 1) / (new_width - 1) if new_width > 1 else 0y_ratio = float(height - 1) / (new_height - 1) if new_height > 1 else 0# 创建网格坐标x_grid = np.linspace(0, width - 1, new_width)y_grid = np.linspace(0, height - 1, new_height)# 获取每个新像素点在原图中的位置x_l = np.floor(x_grid).astype(int)y_l = np.floor(y_grid).astype(int)x_h = np.minimum(x_l + 1, width - 1)y_h = np.minimum(y_l + 1, height - 1)# 计算权重x_weight = x_grid[:, None] - x_l[:, None]y_weight = y_grid[:, None] - y_l[:, None]# 使用numpy索引获取四个邻近像素的值a = img[y_l[:, None], x_l].reshape(new_height, new_width, depth)b = img[y_l[:, None], x_h].reshape(new_height, new_width, depth)c = img[y_h[:, None], x_l].reshape(new_height, new_width, depth)d = img[y_h[:, None], x_h].reshape(new_height, new_width, depth)# 调整权重形状以匹配图像数据x_weight = x_weight[:, :, None]y_weight = y_weight[:, :, None]# 进行双线性插值ab = a * (1 - x_weight.transpose((1, 0, 2))) + b * x_weight.transpose((1, 0, 2))cd = c * (1 - x_weight.transpose((1, 0, 2))) + d * x_weight.transpose((1, 0, 2))result = ab * (1 - y_weight) + cd * y_weightreturn Image.fromarray(np.uint8(result))#方式3:CV2库函数
def bilinear_resize_cv2(img, new_shape):# 将PIL图像转换为numpy数组img_array = np.array(img)# 计算新的尺寸new_height, new_width = new_shape# 使用cv2.resize进行双线性插值start_time = time.time()resized_img = cv2.resize(img_array, (new_width, new_height), interpolation=cv2.INTER_LINEAR)processing_time = time.time() - start_timeprint(f"OpenCV processing time: {processing_time:.4f} seconds")# 将numpy数组转换回PIL图像并返回return Image.fromarray(resized_img)if __name__ == "__main__":# 加载图像img_path = 'image.jpg'original_img = Image.open(img_path)# 设置新的尺寸# new_shape = (original_img.size[0] // 2, original_img.size[1] // 2)new_shape = (640,640)# 使用for循环遍历处理并计时start_time = time.time()resized_img_for_loop= bilinear_resize(original_img, new_shape)numpy_time = time.time() - start_timeprint(f"for-loop processing time: {numpy_time:.4f} seconds")# 使用NumPy广播机制处理并计时start_time = time.time()resized_img_numpy= bilinear_resize_numpy(original_img, new_shape)numpy_time = time.time() - start_timeprint(f"NumPy processing time: {numpy_time:.4f} seconds")# 使用OpenCV处理并计时resized_img_cv2 = bilinear_resize_cv2(original_img, new_shape)# 显示结果(可选)# 创建一个包含三个子图的图形,并设置布局fig, axes = plt.subplots(1, 3, figsize=(15, 5))# 显示第一张图像axes[0].imshow(resized_img_for_loop)axes[0].set_title("Resized with NumPy")axes[0].axis('off')# 显示第二张图像axes[1].imshow(resized_img_numpy)axes[1].set_title("Resized with NumPy (New)")axes[1].axis('off')# 显示第三张图像axes[2].imshow(resized_img_cv2)axes[2].set_title("Resized with OpenCV")axes[2].axis('off')# 调整布局以防止重叠plt.tight_layout()# 显示图像plt.show()
2.2 运行结果
运行结果耗时对比:
for-loop processing time: 3.0354 seconds
NumPy processing time: 0.0666 seconds
OpenCV processing time: 0.0035 seconds

可以看出OpenCV处理速度最快。
- 另外本想尝试支持
OpenCL的pyopencl的加速处理,但是报了点错就没有放代码。
相关文章:
图像缩放的双线性插值实现方式
1、双线性插值概念 双线性插值是一种用于在二维网格上进行插值的方法,适用于图像处理、计算机图形学等领域。它通过利用四个邻近点的已知值,估算出任意点的值。双线性插值在两个方向(通常是水平和垂直)上分别进行线性插值&#x…...
深入剖析 Vue 的响应式原理:构建高效 Web 应用的基石
深入剖析 Vue 的响应式原理:构建高效 Web 应用的基石 在前端开发的广阔天地里,Vue.js 凭借其简洁易用的特性和强大的功能,成为众多开发者的心头好。其中,响应式原理作为 Vue 的核心亮点之一,让数据与视图之间实现了高…...
40.日常算法
1.无重复字符的最长子串 题目来源 给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。 示例 1: 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。 class Solution { public:int lengthOfL…...
CAS单点登录(第7版)11.SSO SLO
如有疑问,请看视频:CAS单点登录(第7版) SSO & SLO 安装IDEA Download IntelliJ IDEA – The IDE for Professional Development in Java and Kotlin 安装Maven Download Apache Maven – Maven MAVEN_HOMED:\apache-maven…...
Bob the Canadian
1:around the house Hi! Bob the Canadian here! Let’s learn English around the house. Come on in! Hi, Bob the Canadian here. Welcome to this video. If this is your first time here, don’t forget to click the subscribe button below, and give…...
CAS单点登录(第7版)16.模仿
如有疑问,请看视频:CAS单点登录(第7版) 模仿 概述 代理身份验证 代理身份验证(模拟),有时称为 Web 的 sudo,是代表其他用户进行身份验证的能力。 在这种情况下,两个参…...
预留:大数据Hadoop之——部署hadoop+hive+Mysql环境(Linux)
传送门目录 前期准备 一、JDK的安装 1、安装jdk 2、配置Java环境变量 3、加载环境变量 4、进行校验 二、hadoop的集群搭建 1、hadoop的下载安装 2、配置文件设置 2.1. 配置 hadoop-env.sh 2.2. 配置 core-site.xml 2.3. 配置hdfs-site.xml 2.4. 配置 yarn-site.xm…...
RabbitMQ介绍以及基本使用
文章目录 一、什么是消息队列? 二、消息队列的作用(优点) 1、解耦 2、流量削峰 3、异步 4、顺序性 三、RabbitMQ基本结构 四、RabbitMQ队列模式 1、简单队列模式 2、工作队列模式 3、发布/订阅模式 4、路由模式 5、主题模式 6、…...
C++演示中介模式
避免两个模块之间的耦合,使用中介模式解决。下面是C代码 #include <iostream> #include <vector>using namespace std;class client;//中介 class mediator { public:void addclient(client* client) {clientVec.push_back(client);}void send(const s…...
Vue的简单入门 一
声明:本版块根据B站学习,创建的是vue3项目,用的是vue2语法风格,仅供初学者学习。 目录 一、Vue项目的创建 1.已安装15.0或更高版本的Node.js 2.创建项目 二、 简单认识目录结构 三、模块语法中的指令 1.v-html 1.文本插值…...
【免费送书活动】《MySQL 9从入门到性能优化(视频教学版)》
本博主免费赠送读者3本书,书名为《MySQL 9从入门到性能优化(视频教学版)》。 《MySQL 9从入门到性能优化(视频教学版)(数据库技术丛书)》(王英英)【摘要 书评 试读】- 京东图书 这本书已经公开…...
export default与export区别
1.定义: export default:用于导出模块中的默认成员。一个模块中只能有一个export default,通常用于导出模块的主要功能或对象。导入时可以使用任意名称,因为它没有具体的名称 export:用于导出模块中的多个成…...
最佳的出牌方法
最佳的出牌方法 真题目录: 点击去查看 E 卷 200分题型 题目描述 手上有一副扑克牌,每张牌按牌面数字记分(J=11,Q=12,K=13,没有大小王),出牌时按照以下规则记分: 出单张,记牌面分数,例如出一张2,得分为2出对或3张,记牌面分数总和再x2,例如出3张3,得分为(3+3+3)x2=1…...
Kotlin 2.1.0 入门教程(二十一)数据类
数据类 数据类主要用于存储数据。 对于每个数据类,编译器会自动生成一些额外的成员函数,这些函数支持将实例打印为易读的输出、比较实例、复制实例等操作。 数据类使用 data 关键字标记: data class User(val name: String, val age: Int…...
30天开发操作系统 第 20 天 -- API
前言 大家早上好,今天我们继续努力哦。 昨天我们已经实现了应用程序的运行, 今天我们来实现由应用程序对操作系统功能的调用(即API, 也叫系统调用)。 为什么这样的功能称为“系统调用”(system call)呢?因为它是由应用程序来调用(操作)系统中的功能来完…...
WEB安全--SQL注入--floor报错注入
一、原理: floor()报错注入需要组合count()、rand()、group by()等函数使用,通过一些手段使数据库在处理语句时产生主键重复的报错,从而达到爆出信息的目的 二、内容: ?id-1 or (select 1 from (select count(*),concat(databa…...
【java面向对象的三大特性】封装、继承和多态
目录标题 一、封装(Encapsulation):二、继承(Inheritance):三、多态(Polymorphism):1. 多态的三个必要条件:2.多态的具体实现:3.多态的使用场景&a…...
Hermite 插值
Hermite 插值 不少实际问题不但要求在节点上函数值相等,而且还要求它的导数值相等,甚至要求高阶导数值也相等。满足这种要求的插值多项式就是 Hermite 插值多项式。 下面只讨论函数值与导数值个数相等的情况。设在节点 a ≤ x 0 < x 1 < ⋯ <…...
【推理llm论文精度】DeepSeek-R1:强化学习驱动LLM推理能力飞跃
最近deepseek R1模型大火,正好复习一下他家的技惊四座的论文https://arxiv.org/pdf/2501.12948 近年来,大型语言模型(LLM)在推理能力上取得了显著进展,但如何进一步有效提升仍然是研究热点。DeepSeek-AI发布了 DeepS…...
arm linux下的中断处理过程。
本文基于ast2600 soc来阐述,内核版本为5.10 1.中断gic初始化 start_kernel() -> init_IRQ() -> irqchip_init() of_irq_init()主要是构建of_intc_desc. 489-514: 从__irqchip_of_table中找到dts node中匹配的of_table(匹配matches->compatible)…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
