当前位置: 首页 > news >正文

使用python进行图像处理—图像标识与NumPy(3)

在Python中进行更复杂的图像处理时,我们通常需要访问和操作图像的像素数据。最常见和高效的方式是将图像数据转换为NumPy数组。NumPy是Python中用于科学计算的核心库,提供了高性能的多维数组对象和大量的数学函数。

Pillow图像对象和NumPy数组之间可以方便地相互转换。

3.1 Pillow Image与NumPy数组的转换
  • Image -> NumPy:使用numpy.array()或np.asarray()函数将Pillow Image对象转换为NumPy数组。
  • NumPy -> Image:使用Image.fromarray()函数将NumPy数组转换回Pillow Image对象。
  • import numpy as np
    from PIL import Image# 假设我们有之前加载的img对象 (例如,一个RGB图像)# 将Pillow Image对象转换为NumPy数组
    # numpy.array() 会创建一个新的数组
    # np.asarray() 会尽量避免复制数据,如果可能的话会返回原始数据的视图
    img_array = np.array(img)# 打印NumPy数组的信息
    print(f"NumPy数组类型: {type(img_array)}")
    print(f"NumPy数组形状 (高, 宽, 通道数): {img_array.shape}")
    print(f"NumPy数组数据类型: {img_array.dtype}")
    # 对于RGB图像,形状通常是 (height, width, 3)
    # 对于灰度图像,形状通常是 (height, width)
    # 对于RGBA图像,形状通常是 (height, width, 4)# 访问像素值
    # 访问位于 (行y, 列x) 的像素值
    # 例如,访问左上角像素 (0, 0)
    top_left_pixel = img_array[0, 0]
    print(f"左上角像素值 (行0, 列0): {top_left_pixel}") # 对于RGB可能是 [R, G, B]# 访问特定通道的值
    # 例如,访问左上角像素的红色通道值 (对于RGB图像)
    if img_array.ndim == 3 and img_array.shape[2] >= 3:top_left_red = img_array[0, 0, 0]print(f"左上角像素的红色通道值: {top_left_red}")# 修改像素值 (直接修改NumPy数组会改变图像数据)
    # 将左上角像素设置为黑色 (对于RGB图像)
    if img_array.ndim == 3 and img_array.shape[2] >= 3:img_array[0, 0] = [0, 0, 0] # 设置为黑色 (R=0, G=0, B=0)print("已将左上角像素值修改为黑色。")# 将NumPy数组转换回Pillow Image对象
    # 需要确保NumPy数组的数据类型和形状与目标Pillow模式兼容
    # 例如,对于RGB模式,需要形状是 (height, width, 3),dtype是 uint8
    # 对于灰度模式,需要形状是 (height, width),dtype是 uint8
    try:# Image.fromarray() 会根据NumPy数组的形状和数据类型自动判断图像模式# 例如,形状(H, W, 3) dtype=uint8 -> RGB# 形状(H, W) dtype=uint8 -> L (灰度)# 形状(H, W, 4) dtype=uint8 -> RGBAimg_from_array = Image.fromarray(img_array)# 保存从NumPy数组转换回来的图像img_from_array.save('output_from_numpy.png')print("NumPy数组已转换回Pillow Image并保存为: output_from_numpy.png")except TypeError as e:# 如果NumPy数组的数据类型或形状与Image.fromarray()的要求不兼容,可能会出现TypeErrorprint(f"错误: 无法从NumPy数组创建Pillow Image对象。原因: {e}")print("请检查NumPy数组的形状和数据类型是否与目标图像模式匹配 (通常是 uint8)。")

    代码解释:

  • import numpy as np:导入NumPy库,并使用别名np。
  • from PIL import Image:导入Pillow的Image模块。
  • img_array = np.array(img):使用np.array()函数将Pillow Image对象img转换为NumPy数组img_array。这将创建一个新的数组来存储图像数据。
  • print(f"NumPy数组类型: {type(img_array)}"):打印img_array的类型,通常是<class 'numpy.ndarray'>。
  • print(f"NumPy数组形状(高,宽,通道数): {img_array.shape}"):打印NumPy数组的形状。对于图像数据,形状通常表示为(height, width, channels)。高度对应行数,宽度对应列数。
  • print(f"NumPy数组数据类型: {img_array.dtype}"):打印NumPy数组中元素的数据类型。对于从图像转换的数组,通常是uint8 (无符号8位整数),值范围是0到255,对应于像素的亮度或颜色通道值。
  • top_left_pixel = img_array[0, 0]:访问NumPy数组的元素。[0, 0]表示访问第一行(索引0)、第一列(索引0)的元素。对于彩色图像,这会返回一个包含该像素所有通道值的数组(如[R, G, B])。对于灰度图像,返回一个单一的亮度值。
  • img_array[0, 0] = [0, 0, 0]:修改NumPy数组中的像素值。这里将左上角像素的RGB值都设置为0,使其变为黑色。**注意:**直接修改img_array会改变图像数据。
  • img_from_array = Image.fromarray(img_array):使用Image.fromarray()函数将NumPy数组img_array转换回Pillow Image对象。Pillow会根据NumPy数组的形状和数据类型自动识别图像模式。
  • img_from_array.save('output_from_numpy.png'):保存从NumPy数组转换回来的图像。
  • try...except TypeError:包含错误处理,因为Image.fromarray()对输入数组的形状和数据类型有特定要求,不匹配时会抛出TypeError。常见的错误是数据类型不是uint8或形状不正确。
3.2利用NumPy进行高效像素操作
  • 将图像数据转换为NumPy数组的最大好处是可以利用NumPy提供的强大数组操作功能,进行高效的像素级处理,而无需遍历每个像素。

  • import numpy as np
    from PIL import Image# 假设 img_array 是一个RGB图像的NumPy数组 (形状为 HxWx3, dtype=uint8)
    # 如果没有img_array,可以先从Pillow Image创建
    # img_array = np.array(img)# 检查是否为彩色图像 (有3个或4个通道)
    if not (img_array.ndim == 3 and (img_array.shape[2] == 3 or img_array.shape[2] == 4)):print("示例需要彩色图像 (RGB 或 RGBA)。请确保加载的图像是彩色。")
    else:# 像素值反转 (负片效果)# 对于uint8类型,最大值是255# 新像素值 = 255 - 原始像素值inverted_array = 255 - img_array# 注意:NumPy的减法操作会自动应用于数组的每个元素# 将反转后的NumPy数组转换回Pillow Imageinverted_img = Image.fromarray(inverted_array.astype('uint8')) # 确保数据类型是uint8inverted_img.save('output_inverted.jpg')print("图像像素值已反转 (负片效果) 并保存为: output_inverted.jpg")# 调整亮度 (增加一个常数值)# 使用np.clip() 确保像素值仍在0-255范围内,避免溢出brightness_offset = 50# 增加亮度brightened_array = img_array + brightness_offset# np.clip(array, min_value, max_value) 将数组中的所有值限制在指定范围内brightened_array_clipped = np.clip(brightened_array, 0, 255)# 将调整亮度的NumPy数组转换回Pillow Imagebrightened_img = Image.fromarray(brightened_array_clipped.astype('uint8'))brightened_img.save('output_brightened.jpg')print("图像亮度已增加并保存为: output_brightened.jpg")# 调整对比度 (乘以一个常数值)# 乘以一个大于1的值增加对比度contrast_factor = 1.5# 乘以对比度因子contrasted_array = img_array * contrast_factor# 同样使用np.clip() 限制像素值contrasted_array_clipped = np.clip(contrasted_array, 0, 255)# 将调整对比度的NumPy数组转换回Pillow Imagecontrasted_img = Image.fromarray(contrasted_array_clipped.astype('uint8'))contrasted_img.save('output_contrasted.jpg')print("图像对比度已调整并保存为: output_contrasted.jpg")# 将彩色图像转换为灰度图像 (使用加权平均法)# 灰度值 = R * 0.2989 + G * 0.5870 + B * 0.1140 (ITU-R BT.601)# 注意:这里假设 img_array 是 RGB (最后一维大小为 3)if img_array.shape[2] == 3:# 使用NumPy的广播功能和点乘# [:, :, 0] 获取所有像素的R通道# [:, :, 1] 获取所有像素的G通道# [:, :, 2] 获取所有像素的B通道# 将这三个二维数组与对应的权重相乘,然后相加# .sum(axis=2) 对最后一个轴(通道轴)求和# .astype('uint8') 将结果转换为uint8类型grayscale_array = (img_array[:, :, 0] * 0.2989 +img_array[:, :, 1] * 0.5870 +img_array[:, :, 2] * 0.1140).astype('uint8')# 将灰度NumPy数组 (形状为 HxW) 转换回Pillow Image (L模式)grayscale_img = Image.fromarray(grayscale_array, 'L') # 明确指定模式为'L'grayscale_img.save('output_grayscale_numpy.jpg')print("彩色图像已使用NumPy转换为灰度并保存为: output_grayscale_numpy.jpg")else:print("跳过灰度转换示例,因为它需要RGB图像。")

  • import numpy as np:导入NumPy。
  • from PIL import Image:导入Pillow。
  • img_array = np.array(img):将Pillow Image对象转换为NumPy数组。
  • inverted_array = 255 - img_array:对NumPy数组进行减法运算。NumPy支持数组的广播(broadcasting),这里的255会被广播到与img_array相同的形状,然后进行逐元素减法。结果是每个像素的每个通道值都变为255 -原始值,实现了负片效果。
  • inverted_img = Image.fromarray(inverted_array.astype('uint8')):将处理后的NumPy数组转换回Pillow Image。.astype('uint8')确保数组的数据类型是uint8,这是图像像素的标准类型。
  • brightened_array = img_array + brightness_offset:增加亮度。将一个常数加到数组的每个元素上。
  • brightened_array_clipped = np.clip(brightened_array, 0, 255): np.clip()函数将数组中的所有值限制在指定的最小值和最大值之间。这里用于确保像素值不会超出0-255的有效范围,避免溢出导致意外结果。
  • contrasted_array = img_array * contrast_factor:调整对比度。将数组的每个元素乘以一个常数。
  • contrasted_array_clipped = np.clip(contrasted_array, 0, 255):同样使用np.clip()限制像素值。
  • grayscale_array = (img_array[:, :, 0] * 0.2989 + ...).astype('uint8'):这是将彩色图像转换为灰度的NumPy实现。
  • img_array[:, :, 0]:使用切片(slicing)获取所有行、所有列的第一个通道(红色通道)的数据,结果是一个二维NumPy数组。
  • 0.2989:将红色通道的二维数组与权重相乘,NumPy会执行逐元素的乘法。
  • 类似的获取绿色([:, :, 1])和蓝色([:, :, 2])通道的数据并乘以各自的权重。
  • +:将三个加权后的二维数组相加,NumPy执行逐元素的加法。
  • .sum(axis=2):这一部分在我的解释中写错了,正确的灰度转换是直接加权求和,不需要sum(axis=2)。对于形状为(H, W, 3)的数组,img_array[:, :, 0]是(H, W)的数组,乘以常数后仍然是(H, W)。将三个(H, W)数组相加,结果仍然是(H, W)的数组,这正是灰度图像的形状。正确的写法是直接相加。
  • .astype('uint8'):将最终的浮点数结果转换为uint8整数类型。
  • grayscale_img = Image.fromarray(grayscale_array, 'L'):将形状为(H, W)且数据类型为uint8的NumPy数组转换回Pillow Image对象,并明确指定模式为'L' (灰度)。
  • 通过将图像转换为NumPy数组,我们可以利用NumPy强大的矢量化运算能力,高效地执行各种像素级别的数学操作,这比使用循环遍历每个像素要快得多。
http://www.xdnf.cn/news/951913.html

相关文章:

  • 【PDF识别改名】PDF指定区域OCR识别重命名工具使用教程和注意事项
  • 前缀和题目:寻找数组的中心下标
  • NoSQL 之 Redis 集群
  • JS红宝书笔记 10.6 - 10.10 函数
  • 树莓派超全系列教程文档--(60)树莓派摄像头操作命令及使用其一
  • Cyber Weekly #59
  • 如何在网页里填写 PDF 表格?
  • MyBatis中关于缓存的理解
  • Spring Framework 6:核心升级特性
  • 2023赣州旅游投资集团
  • OptiStruct结构分析与工程应用:传递路径贡献量分析(TPA)
  • 接口 RESTful 中的超媒体:REST 架构的灵魂驱动
  • 数据集分享 | MOT17数据集、UAVDT数据集
  • qt 双缓冲案例对比
  • 面试高频问题
  • 魔兽世界正式服插件与宏-敏锐盗贼实用宏探索(1)-宏命令制作入门与基本知识
  • 从面试角度回答Android中ContentProvider启动原理
  • android13 app的触摸问题定位分析流程
  • 邮科ODM摄像头:多维度护航高铁安全系统方案解析
  • Kubernetes ClusterIP 端口深度解析:虚拟服务与流量转发机制
  • 我的世界Java版1.21.4的Fabric模组开发教程(十三)自定义方块状态
  • 椭圆曲线密码学(ECC)
  • 基于ADMM的MRI-PET高质量图像重建算法
  • 【Linux】进程间通讯-消息队列
  • PHP:Web 开发的经典利器
  • 我如何使用 CodeMCP 进行开发并控制其他编程助手的预算
  • nodejs express 打包部署
  • VR 技术赋能南锣鼓巷的多元发展潜力与前景​
  • 多模态图像修复系统:基于深度学习的图片修复实现
  • Android Kotlin 协程详解