Python 中常用图像数据结构

(原文:https://blog.iyatt.com/?p=13222 )

1 测试环境

Python 3.12.1

numpy 1.26.3
opencv-python 4.9.0.80
pillow 10.2.0
matplotlib 3.8.2

注:

  • 基于 2022.1.16 和 2022.4.9 的三篇博文再次验证并重写,原文已删除
  • 测试使用的图片文件为 AI 绘制

2 图像数据结构

2.1 OpenCV 打开图片并显示

Python 版 OpenCV 中图像数据是用的 NumPy 数组存储,通道顺序为 BGRA(蓝 绿 红 透明度),三通道则为 BGR。

import cv2image_path = 'demo.png' # 图片路径img = cv2.imread(image_path) # 打开图片文件
cv2.imshow('my image', # 窗口标题img) # 图像数据
cv2.waitKey(0) # 阻塞窗口,按任意键继续
cv2.destroyAllWindows() # 关闭所有窗口

file

2.2 Matplotlib 打开图片并显示

Matplotlib 和 OpenCV 一样都是采用的 NumPy 数组存储图像数据,只是通道顺序为 RGB。

import matplotlib.pyplot as pltimage_path = 'demo.png'image = plt.imread(image_path)
plt.axis('off') # 不显示坐标轴
plt.imshow(image)
plt.show()

file

2.3 Pillow 打开图片用 OpenCV 显示

Pillow 是 Python 中较为常用的图像库。

from PIL import Image
import cv2
import numpy as npimage_path = 'demo.png'pillow_image = Image.open(image_path)
opencv_image = cv2.cvtColor(np.array(pillow_image), # Pillow 图像数据结构转 NumPycv2.COLOR_RGB2BGR # 通道顺序由 RGB 转为 BGR
)
cv2.imshow('Pillow Image To OpenCV Image', opencv_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.4 OpenCV 打开图片用 Tkinter 显示(OpenCV 转 Pillow)

Tkinter 是 Python 的官方 GUI 库,Pillow 的图像数据支持直接在 Tkinter 中显示,因此这里把 OpenCV 图像转为 Pillow 再到 Tkinter 中显示。

import cv2
import tkinter as tk
from PIL import Image, ImageTkimage_path = 'demo.png'class Application(tk.Frame):def __init__(self, master):super().__init__(master)self.master = masterdef interface(self):global pillow_image # 注意 Tkinter 显示的图片要使用全局变量opencv_image = cv2.imread(image_path)pillow_image = ImageTk.PhotoImage(Image.fromarray(cv2.cvtColor(opencv_image,cv2.COLOR_BGR2RGB)))tk.Label(self.master, image=pillow_image).pack()if __name__ == '__main__':root = tk.Tk()root.title('OpenCV 打开图片并在 Tkinter 中显示') # 窗口标题app = Application(root)app.interface()root.mainloop()

file

2.5 Pillow 打开图片并使用 Tkinter 显示

import tkinter as tk
from PIL import Image, ImageTkimage_path = 'demo.png'class Application(tk.Frame):def __init__(self, master):super().__init__(master)self.master = masterdef interface(self):global pillow_image # 注意 Tkinter 显示的图片要使用全局变量pillow_image = ImageTk.PhotoImage(Image.open(image_path))tk.Label(self.master, image=pillow_image).pack()if __name__ == '__main__':root = tk.Tk()root.title('Pillow 打开图片并在 Tkinter 中显示')app = Application(root)app.interface()root.mainloop()

2.6 Matplotlib 打开图片用 OpenCV 显示

Matplotlib 和 OpenCV 都是使用 NumPy 数组保存图像数据,两者转换只需要修改通道顺序即可,非常方便。

import matplotlib.pyplot as plt
import cv2image_path = 'demo.png'matplotlib_image = plt.imread(image_path)
opencv_image = cv2.cvtColor(matplotlib_image,cv2.COLOR_RGB2BGR
)cv2.imshow('Matplotlib To OpenCV',opencv_image    
)
cv2.waitKey(0)
cv2.destroyAllWindows()

2.7 OpenCV 打开图片用 Matplotlib 显示

import matplotlib.pyplot as plt
import cv2image_path = 'demo.png'opencv_image = cv2.imread(image_path)
matplotlib_image = cv2.cvtColor(opencv_image,cv2.COLOR_RGB2BGR
)plt.imshow(matplotlib_image)
plt.axis('off')
plt.show()

2.8 Matplotlib 打开图片用 Tkinter 显示

import matplotlib.pyplot as plt
import tkinter as tk
from PIL import Image, ImageTkimage_path = 'demo.png'class Application(tk.Frame):def __init__(self, master):super().__init__(master)self.master = masterdef interface(self):global pillow_image # 注意 Tkinter 显示的图片要使用全局变量matplotlib_image = plt.imread(image_path)pillow_image = ImageTk.PhotoImage(Image.fromarray((matplotlib_image * 255).astype('uint8') # 把 float32 转为 uint8))tk.Label(self.master, image=pillow_image).pack()if __name__ == '__main__':root = tk.Tk()root.title('Matplotlib 打开图片并在 Tkinter 中显示') # 窗口标题app = Application(root)app.interface()root.mainloop()

3 基于 NumPy 数组的图像数据结构操作

OpenCV 和 Matplotlib 中图像数据都是使用 NumPy,这里试着创建一个 NumPy 数组来操作,更好理解其结构。

这里创建一个 2x2 分辨率的图片,4 个点分别定义为黑色RGB(0,0,0),白色RGB(255,255,255),红色RGB(255,0,0),紫色RGB(255,0,255),先用 Matplotlib 示例,通道顺序就是 RGB

import matplotlib.pyplot as plt
import numpy as npdata = np.array([[[0, 0, 0], [255, 255, 25]],[[255, 0, 0], [255, 0, 255]]
])print('形状:', data.shape)
plt.imshow(data)
plt.show()

file
形状是 2x2 分辨率,3 通道(RGB)
file

对这种图像数据结构的切片操作格式如下

image[y1:y2:ys, x1:x2:xs, c1:c2:cs]

逗号分隔的三部分分别是操作 y 轴、x 轴、颜色通道,每部分冒号分隔的 1 和 2 对应起始和结束,s 对应步长,可以省略。

3.1 颜色通道顺序转换

前面 Matplotlib 和 OpenCV 图像数据互相转换是使用的 OpenCV 的 cvtColor 函数,这里可以尝试基于 NumPy 数组操作,将 cs 设为 -1,则会逆向通道顺序。
Matplotlib 显示图像会自动调整比例,但是 OpenCV 会原比例显示,所以这里需要放大图像再显示。

import cv2
import numpy as npdata = np.array([[[0, 0, 0], [255, 255, 25]],[[255, 0, 0], [255, 0, 255]]
], dtype=np.uint8)new_data = data[:,:,::-1] # 通道顺序逆向
new_data = cv2.resize(new_data,(255, 255), # 放大后的分辨率interpolation=cv2.INTER_NEAREST # 最近邻插值法,直接复制原图像像素,不计算衔接边缘
)
cv2.imshow('my data', new_data)
cv2.waitKey(0)
cv2.destroyAllWindows()

file

3.2 图像部分截取

import matplotlib.pyplot as pltimage_path = 'demo.png'img = plt.imread(image_path)
plt.axis('off')roi = img[14:556, 219:633] # 截取 y 取值 14~556,x 取值到 219~633 的部分
plt.imshow(roi)
plt.show()

file

3.3 颜色通道分离

3.3.1 OpenCV

下面的示例中从图片文件读取,然后将图像数据的三色通道分离,另外创建一个等大小的空数据通道,然后再尝试用空数据填充 G、B 通道和分离出来的 R 通道合并生成一个新的彩色图片,新生成的图片中缺失了绿色和蓝色通道则变为了“黑红”图片。

import cv2
import numpy as npimage_path = 'demo.png'image = cv2.imread(image_path)
B = image[:, :, 0:1] # 截取 0 通道[0,1),前开后闭,即蓝色
G = image[:, :, 1:2] # 截取 1 通道,绿色
R = image[:, :, 2:3] # 截取 2 通道,红色
ZERO = np.zeros(B.shape, dtype=B.dtype) # 创建一个空数据的通道R_image = cv2.merge([ZERO, ZERO, R])
cv2.imshow('R image', R_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

file

3.3.2 Matplotlib

import matplotlib.pyplot as plt
import numpy as npimage_path = 'demo.png'image = plt.imread(image_path)
R = image[:, :, 0] # 直接截取单个通道,或者 0:1 也行
G = image[:, :, 1]
B = image[:, :, 2]
ZERO = np.zeros(B.shape, dtype=R.dtype) # 创建一个空数据的通道B_image = np.dstack((ZERO, ZERO, B))
plt.imshow(B_image)
plt.axis('off')
plt.show()

file

3.4 深拷贝和浅拷贝

图像数据的深拷贝和浅拷贝,在基于 NumPy 数组的前提下,也就是 NumPy 数组的深拷贝和浅拷贝。直接用等号赋值实际得到的是为原数组起的一个别名,通过原来的数组名和新起的名字操作的都是同一块地址,实际就是浅拷贝。使用 copy 方法拷贝则是深拷贝,深拷贝不是创建一个别名,而是新开辟空间,并复制原来数组的数据到新空间,新旧数组是独立的空间。

import numpy as nparray = np.array([[[0, 0, 255]]
])array1 = array
array2 = array.copy()print('array 地址/是否只读:', array.__array_interface__['data'])
print('array1 地址/是否只读:', array1.__array_interface__['data'])
print('array2 地址/是否只读:', array2.__array_interface__['data'])

file

3.5 贴图

import matplotlib.pyplot as pltimage_path = 'demo.png'
src = plt.imread(image_path)copy_image = src.copy() # 深拷贝
copy_image[628:810, 194:548] = [255, 255, 255] # x:194~548。y:628~810 填充为白色RGB(255,255,255)
copy_image[713:1017, 239:478] = src[104:408, 289:528] # 截取原图人脸部分 x:289~528,y:104~408,贴到拷贝图像的 x:239~478,y:713~1017plt.axis('off')
plt.imshow(copy_image)
plt.show()

file

3.6.1 透明度通道

前面的操作都是前三个基色的通道,没有涉及第 4 个通道透明度,下面这张胡子图片就是具有 4 通道的图片,可以右键另存为用于测试。
file

import matplotlib.pyplot as plt
import numpy as npimage_path = 'demo.png'
beard_path = 'demo1.png' # 胡子图片文件src = plt.imread(image_path)
beard = plt.imread(beard_path)# 为原图添加 alpha 通道(透明度)
image_with_alpha = np.dstack([src,np.ones((src.shape[0], src.shape[1]), dtype=src.dtype)
])beard_h, beard_w = beard.shape[:2] # 获取胡子图片的尺寸
mask_boolean = beard[:, :, 3] == 1 # alpha 值为 1 的像素点即为完全不透明的值
image_with_alpha[523:523+beard_h, 94:94+beard_w][mask_boolean] = beard[mask_boolean] # 将胡子不透明的部分像素值嵌入图像中plt.axis('off')
plt.imshow(image_with_alpha)
plt.show()

file

结合透明度信息后,就不会完全照搬把贴的图片拿上去挡住,不透明的部分就显示原图的内容。这里使用 Matplotlib 读取的图片数据类型为 float32(OpenCV 是 uint8,为 0-255 的整数),每个通道的像素点数据为 0-1 的小数,alpha 通道为 1 就是完全呈现 RGB 的值,alpha 为 0 就是完全不呈现 RGB 值,中间就是过渡。
上面写的例子其实很有局限性,用的胡子图片比较特殊,透明度的值是极化的,要么完全透明,要么完全不透明,所以可以采用上面的方法判断不透明的就直接复制替换原图的部分,但是如果透明度是 0-1 之间的不完全透明,也不是完全不透明,就不能用这种方法处理。不完全透明的情况下,贴上去的图不能完全遮挡原图,也就是原图和贴上去的图的像素值信息都要显示出来。

要解决上面提到的问题就得从透明度本身的性质着手,先只考虑一个像素点的情况,假如原图的像素点值为[1, 0, 0, 1],就是完全显示红色的点,然后我要将一个 [0, 0, 1, 0.6] 的点贴上去,这个点本身是纯蓝色,但是透明度为 0.6,即只呈现蓝色的 60%,那么剩下的 40% 就显示背景(即原图),那么最终显示的就应该是$$[1 \times 0.4 + 0 \times 0.6, 0 \times 0.4 + 0 \times 0.6, 0 \times 0.4 + 1 \times 0.6, 1 \times 0.4 + 0.6 \times 0.6]$$,就有下面的代码:

import matplotlib.pyplot as plt
import numpy as npimage_path = 'demo.png'
beard_path = 'demo1.png' # 胡子图片文件src = plt.imread(image_path)
beard = plt.imread(beard_path)# 为原图添加 alpha 通道(透明度)
image_with_alpha = np.dstack([src,np.ones((src.shape[0], src.shape[1]), dtype=src.dtype)
])beard_h, beard_w = beard.shape[:2] # 获取胡子图片的尺寸beard_alpha1 = beard[:, :, 3] # 取出胡子图片的透明度
beard_alpha2 = 1 - beard_alpha1 # 计算出原图被贴图位置应该具有的透明度
for c in range(4):image_with_alpha[523:523+beard_h, 94:94+beard_w, c] = beard_alpha2 * image_with_alpha[523:523+beard_h, 94:94+beard_w, c] + beard_alpha1 * beard[:, :, c]
plt.axis('off')
plt.imshow(image_with_alpha)
plt.show()

这里我也不能保证我的思路是对的,只是使用胡子图片验证没问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/250167.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Stable diffusion使用和操作流程

Stable Diffusion是一个文本到图像的潜在扩散模型,由CompVis、Stability AI和LAION的研究人员和工程师创建。它使用来自LAION-5B数据库子集的512x512图像进行训练。使用这个模型,可以生成包括人脸在内的任何图像,因为有开源的预训练模型,所以我们也可以在自己的机器上运行它…

网络时间协议NTP工作模式

单播服务器/客户端模式 单播服务器/客户端模式运行在同步子网中层数较高层上。这种模式下,需要预先知道服务器的IP地址。 客户端:运行在客户端模式的主机(简称客户端)定期向服务器端发送报文,报文中的Mode字段设置为3(客户端模式)。当客户端接收到应答报文时,客户端会…

编程流程图

对于复杂流程,我做开发之前一般会 先画一下流程图。特别是多个部门有交叉的情况下: processOn: 这个是我之前 一直的选择,他可以画上面的这些,流程图,网页操作,但是他不是免费的,查过…

【数据分享】1929-2023年全球站点的逐年最高气温数据(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、湿度等指标,其中又以气温指标最为常用!说到气温数据,最详细的气温数据是具体到气象监测站点的气温数据! 之前我们分享过1929-2023年全球气象站…

【微信小程序开发】小程序的事件处理和交互逻辑(最详细)

前言 在微信小程序中,事件处理和交互逻辑是开发过程中非常重要的环节,它们直接影响到用户体验和功能实现。今天为大家继续详解小程序的事件处理和交互逻辑 文章目录 前言为什么要学习事件处理和交互逻辑?事件处理基础事件类型和触发条件事件绑…

python3.8 安装缺少ssl、_ctypes模块解决办法

问题 安装pyhton3.8安装默认不依赖ssl 运行Flask项目时报错&#xff1a; Traceback (most recent call last):File "/usr/local/python3/bin/flask", line 8, in <module>sys.exit(main())File "/usr/local/python3/lib/python3.8/site-packages/flask…

Vue3_基础使用_1

这节主要介绍&#xff1a; vue2与vue3的区别&#xff0c;创建响应式的数据&#xff0c;setup语法糖的使用&#xff0c;watch监听&#xff0c;及vue3创建项目。 vue2的选项式与vue3的组合式区别&#xff1a; 选项式&#xff1a;vue2中数据与方法计算属性等等&#xff0c;针对…

Java学习-面向对象-继承

继承是什么&#xff1f; 示例&#xff1a; packagejava_jicheng_demo1; publicclassA{ //创建公开的成员变量&#xff0c;方法 publicStringnane; publicintage; publicvoidprint1(){ System.out.println("666"); } //创建私有的成员变量&#xff0c;方法 privateStr…

#《AI中文版》V3 第 3 章 知情搜索

参考链接&#xff1a; [1] 开源内容&#xff1a;https://github.com/siyuxin/AI-3rd-edition-notes [2] Kimi Chat官网链接 正文笔记 P90 针对 大型问题。 知情搜索&#xff08;informed search&#xff0c;也称有信息搜索&#xff09;&#xff1a;利用启发式方法&#xff0c…

MobPush:Android SDK 集成指南

开发工具&#xff1a;Android Studio 集成方式&#xff1a;Gradle在线集成 安卓版本支持&#xff1a;minSdkVersion 19 集成前准备 注册账号 使用PushSDK之前&#xff0c;需要先在MobTech官网注册开发者账号&#xff0c;并获取MobTech提供的AppKey和AppSecret&#xff0c;…

基于python+django,我开发了一款药店信息管理系统

功能介绍 平台采用B/S结构&#xff0c;后端采用主流的Python语言进行开发&#xff0c;前端采用主流的Vue.js进行开发。 功能包括&#xff1a;药品管理、分类管理、顾客管理、用户管理、日志管理、系统信息模块。 代码结构 server目录是后端代码web目录是前端代码 部署运行…

MySQL 索引和事务

目录 1 索引1.1 简介1.2 使用1.3 示例 2 事务2.1 简介2.2 使用 1 索引 1.1 简介 索引是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引&#xff0c;并指定索引的类型&#xff0c;各类索引有各自的数据结构实现。 索引底层是…

PYTHON蓝桥杯——每日一练(简单题)

题目 求123...n的值。 输入格式 输入包括一个整数n。 输出格式 输出一行&#xff0c;包括一个整数&#xff0c;表示123...n的值。 提示 说明&#xff1a;请注意这里的数据规模。 本题直接的想法是直接使用一个循环来累加&#xff0c;然而&#xff0c;当数据规模很大时&…

【算法详解 | 二分查找】详解二分查找 \ 折半查找高效搜索算法 | 顺序数组最快搜索算法 | 递归循环解决二分查找问题

二分查找 by.Qin3Yu 本文需要读者掌握 顺序表 的操作基础&#xff0c;完整代码将在文章末尾展示。 顺序表相关操作可以参考我的往期博文&#xff1a; 【C数据结构 | 顺序表速通】使用顺序表完成简单的成绩管理系统.by.Qin3Yu 文中所有代码使用 C 举例&#xff0c;且默认已使用…

Dash :一个超漂亮的 python Web库!

你好&#xff0c;Dash 是一个非常方便的 Python 库&#xff0c;它可以非常非常帮助你构建基于 Web 的应用程序&#xff0c;而且最棒的是你无需使用 JavaScript&#xff01; 不仅如此&#xff0c;Dash 还是一个专门用于创建分析 Web 应用程序的用户界面库。 如果你是一个使用 …

前端JavaScript篇之对对象与数组的解构的理解、如何提取高度嵌套的对象里的指定属性?

目录 对对象与数组的解构的理解如何提取高度嵌套的对象里的指定属性&#xff1f; 对对象与数组的解构的理解 对象与数组的解构是一种通过模式匹配的方式&#xff0c;从对象或数组中提取值&#xff0c;并将其赋给变量的过程。它可以让我们以一种简洁的方式访问和使用对象或数组…

嵌入式学习第十六天

C语言小项目&#xff1a; 制作俄罗斯方块小游戏&#xff08;1&#xff09; 主函数部分&#xff08;1&#xff09; #include <stdio.h> #include <unistd.h> #include <signal.h>extern int InitBoarder(void); extern int SetBoarder(void); extern int S…

2023年算法SAO-CNN-BiLSTM-ATTENTION回归预测(matlab)

2023年算法SAO-CNN-BiLSTM-ATTENTION回归预测&#xff08;matlab&#xff09; SAO-CNN-BiLSTM-Attention雪消融优化器优化卷积-长短期记忆神经网络结合注意力机制的数据回归预测 Matlab语言。 雪消融优化器( SAO) 是受自然界中雪的升华和融化行为的启发&#xff0c;开发了一种…

Unity DOTween插件常用方法(二)

文章目录 1.3 动画设置1.4 动画队列 Sequence1.5 动画回调函数1.6 等待函数&#xff08;协程中使用&#xff09; 1.3 动画设置 SetLoops 设置循环动画&#xff1b; 参数&#xff1a; loops&#xff1a;指定循环的次数&#xff0c;设置为 -1 表示无限循环&#xff1b; loopType…

由数据插入超长引起的问题——了解GaussDB和openGauss的字符集

前言 故事是这样开始的。我们的小DEMO项目的数据库版本从openGauss 2.1.0升级到了5.0.0版本。升级后进行功能验证的时候&#xff0c;测试同学发现个BUG&#xff0c;原来通过gs_restore导出来的数据再导入时报超长&#xff0c;插入失败了&#xff0c;如下图所示&#xff0c;nva…