图像缩放的双线性插值实现方式
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)…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
