PIL 与 Pillow:Python 图像处理完全指南 – wiki基地


PIL 与 Pillow:Python 图像处理完全指南

在数字时代,图像无处不在。从社交媒体上的照片到科学研究中的卫星影像,图像处理已经成为一项核心技能。对于 Python 开发者而言,处理图像是一个常见的需求,而 PIL(Python Imaging Library)及其现代继任者 Pillow 则是实现这一目标的标准库。

本文将带你深入了解 PIL 和 Pillow 的世界,从它们的历史渊源讲起,逐步掌握如何安装、使用 Pillow 进行各种常见的图像处理操作,包括加载、保存、缩放、裁剪、旋转、色彩调整、滤镜应用,甚至是更高级的绘制和像素操作。无论你是初学者还是有一定经验的开发者,本文都将为你提供一个全面的指南。

1. 历史的回响:PIL 与 Pillow 的故事

要理解 Pillow,首先需要回顾一下 PIL。PIL 是由 Fredrik Lundh 创建的一个强大的图像处理库,它在 Python 社区中迅速流行起来,成为了进行图像操作的首选工具。PIL 提供了一系列强大的功能,可以处理多种图像格式,并支持像素操作、色彩空间转换、过滤等多种图像处理任务。

然而,随着时间的推移,PIL 的开发逐渐停滞。它最后一个正式版本 1.1.7 发布于 2009 年,并且不支持 Python 3。这对于日益壮大的 Python 3 用户群体来说是一个巨大的障碍。社区需要一个能够持续维护、支持新特性和现代 Python 版本的图像库。

正是在这样的背景下,Pillow 应运而生。Pillow 是 PIL 的一个友好分支(friendly fork),由 Alex Clark 和其他贡献者共同维护。Pillow 的目标是提供一个易于安装、支持 Python 3、并且兼容 PIL 接口的现代图像处理库。Pudoh 团队(Pillow 的维护者)成功地使 Pillow 成为了 PIL 的事实上的继任者,并且在 PIL 的基础上增加了许多改进和新功能,例如更好的文件格式支持、更简单的安装过程等。

今天的选择:永远是 Pillow。 当你在 Python 中需要进行图像处理时,你应该毫无疑问地选择安装和使用 Pillow,而不是尝试寻找并安装老旧的 PIL 库。Pillow 完整地保留了 PIL 的 API,这意味着大多数为 PIL 编写的代码都可以无缝地迁移到 Pillow 上运行。

2. 万事开头难?Pillow 的安装

安装 Pillow 非常简单,通常只需要一个命令。由于 Pillow 在 PyPI(Python Package Index)上以 Pillow 的名称发布,而不是 PIL,所以安装命令如下:

bash
pip install Pillow

如果你使用的是特定的 Python 环境(如虚拟环境),请确保在相应的环境中执行此命令。

安装成功后,你可以通过导入 PIL 模块来验证:

python
try:
from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageEnhance, ImageOps
print("Pillow (PIL) 模块导入成功!")
except ImportError:
print("Pillow (PIL) 模块导入失败,请检查是否正确安装。")

如果导入成功并输出了提示信息,那么恭喜你,Pillow 已经准备就绪了!

3. Pillow 的核心:Image 对象与图像模式

在 Pillow 中,所有图像处理操作都围绕着 PIL.Image.Image 类展开。这个类代表了一个图像对象,它包含了图像的像素数据、尺寸、模式等信息。你可以通过各种方法创建或打开一个 Image 对象,然后调用其方法来执行各种操作。

图像模式 (Image Modes)

图像模式决定了像素的构成以及如何解读像素值。理解模式对于正确的图像处理至关重要。Pillow 支持多种模式,常见的包括:

  • 1 (1-bit pixels, black and white): 每个像素只有黑白两种颜色,通常用于存储扫描的文本或黑白图像。
  • L (8-bit pixels, grayscale): 每个像素用一个字节表示,0 为黑色,255 为白色,中间值表示不同灰度。
  • P (8-bit pixels, using a palette): 每个像素用一个字节表示,这个字节是一个索引,指向一个 256 色的调色板。常用于 GIF 和 PNG 文件,支持透明。
  • RGB (3×8-bit pixels, true color): 每个像素由红、绿、蓝三个通道组成,每个通道用一个字节表示(0-255)。这是最常见的彩色图像模式。
  • RGBA (4×8-bit pixels, true color with alpha):RGB 模式的基础上增加了一个 Alpha 通道,用于表示像素的透明度。0 表示完全透明,255 表示完全不透明。
  • CMYK (4×8-bit pixels, color separation): 青、洋红、黄、黑四种颜色通道,主要用于印刷。
  • YCbCr (3×8-bit pixels, color video format): 常用于视频和 JPEG 压缩。
  • I (32-bit integer pixels): 整数像素模式。
  • F (32-bit floating point pixels): 浮点数像素模式,适用于一些科学计算或图像处理算法。

你可以通过图像对象的 mode 属性获取图像的模式:

“`python
from PIL import Image

try:
img = Image.open(“test_image.jpg”) # 假设你有一个 test_image.jpg 文件
print(f”图像模式: {img.mode}”)
print(f”图像尺寸: {img.size}”) # (宽度, 高度)
print(f”图像格式: {img.format}”)
except FileNotFoundError:
print(“请准备一个名为 test_image.jpg 的图片文件进行测试。”)
except Exception as e:
print(f”发生错误: {e}”)

“`

4. 打开与保存图像

图像处理的第一步通常是打开一个现有的图像文件,或者创建一个新的图像。处理完成后,你需要将结果保存到文件中。

打开图像

使用 PIL.Image.open() 函数可以打开多种格式的图像文件:

“`python
from PIL import Image

try:
# 打开一个 JPEG 文件
img_jpg = Image.open(“photo.jpg”)
print(f”打开 photo.jpg 成功,格式: {img_jpg.format}, 模式: {img_jpg.mode}, 尺寸: {img_jpg.size}”)

# 打开一个 PNG 文件
img_png = Image.open("logo.png")
print(f"打开 logo.png 成功,格式: {img_png.format}, 模式: {img_png.mode}, 尺寸: {img_png.size}")

# Pillow 会自动识别文件格式。
# 你也可以显式指定加载器,但这通常是不必要的:Image.open("image.bmp", "BMP")

except FileNotFoundError as e:
print(f”文件未找到: {e}”)
except Exception as e:
print(f”打开文件时发生错误: {e}”)

“`

Image.open() 返回一个 Image 对象。如果文件不是有效的图像文件,或者格式不受支持,它将抛出异常。

创建新图像

使用 PIL.Image.new() 函数可以创建一个全新的图像对象:

“`python
from PIL import Image

创建一个 200×100 像素的 RGB 模式纯红色图像

参数:mode, size (width, height), color (optional)

img_red = Image.new(“RGB”, (200, 100), “red”) # 或 (255, 0, 0)
img_red.show() # 显示图像 (需要安装一个图像查看器)

创建一个 100×100 像素的 L 模式纯灰色图像 (灰度值为 128)

img_gray = Image.new(“L”, (100, 100), 128)
img_gray.show()

创建一个带透明通道的 RGBA 图像,背景完全透明 (0,0,0,0)

img_transparent = Image.new(“RGBA”, (150, 150), (0, 0, 0, 0))
img_transparent.show()
“`

Image.new() 的第三个参数 color 是可选的,用于指定图像的背景颜色。对于颜色,你可以使用字符串(如 “red”, “blue”, “white”, “black”),或者一个元组表示像素值(如 (255, 0, 0) 对于 RGB 红色,128 对于 L 模式灰度,(0, 0, 0, 0) 对于 RGBA 完全透明)。颜色的表示方式取决于图像的模式。

保存图像

使用图像对象的 save() 方法可以将图像保存到文件:

“`python
from PIL import Image

try:
img = Image.open(“photo.jpg”) # 假设 photo.jpg 存在

# 保存为 PNG 格式,Pillow 会根据文件扩展名自动选择格式
img.save("photo_copy.png")
print("图像保存为 photo_copy.png 成功。")

# 显式指定保存格式 (不推荐,除非你不需要文件扩展名)
img.save("photo_copy_jpeg", format="JPEG")
print("图像保存为 photo_copy_jpeg (JPEG 格式) 成功。")

# 保存为 JPEG 格式并设置质量 (质量范围 1-95,默认约 75)
# 注意:质量选项仅适用于 JPEG 格式
img.save("photo_low_quality.jpg", quality=20)
print("图像保存为 photo_low_quality.jpg (低质量 JPEG) 成功。")

# 保存为 PNG 格式并优化 (通常会减小文件大小,但不影响图像质量)
# 注意:优化选项仅适用于 PNG 格式
img.save("photo_optimized.png", optimize=True)
print("图像保存为 photo_optimized.png (优化 PNG) 成功。")

except FileNotFoundError:
print(“请准备一个名为 photo.jpg 的图片文件进行测试保存。”)
except Exception as e:
print(f”保存文件时发生错误: {e}”)

“`

save() 方法的第一个参数是保存的文件名。Pillow 通常会根据文件扩展名来判断要使用的文件格式。你也可以通过 format 参数强制指定格式。不同的文件格式支持不同的选项(如 JPEG 的 quality,PNG 的 optimize)。

5. 基础图像操作:缩放、裁剪、旋转

Pillow 提供了简单易用的方法来执行常见的图像几何变换。

缩放 (Resizing)

使用 image.resize() 方法可以改变图像的尺寸。它接受一个新的尺寸元组 (width, height) 作为参数,并返回一个新的图像对象。

“`python
from PIL import Image

try:
img = Image.open(“photo.jpg”)
print(f”原始尺寸: {img.size}”)

# 将图像缩小到 128x128 像素
# resize 方法返回一个新的 Image 对象,原始图像不受影响
img_small = img.resize((128, 128))
print(f"缩小后尺寸: {img_small.size}")
img_small.save("photo_small.jpg")

# 将图像放大到 800x600 像素
img_large = img.resize((800, 600))
print(f"放大后尺寸: {img_large.size}")
img_large.save("photo_large.jpg")

# 缩放时指定滤波器 (可选,用于提高缩放质量,特别是缩小)
# Image.LANCZOS, Image.BILINEAR, Image.BICUBIC, Image.NEAREST 等
img_resized_hq = img.resize((300, 200), Image.Resampling.LANCZOS) # Pillow 9.0+ 使用 Image.Resampling
# 旧版本 PIL/Pillow < 9.0 使用 Image.LANCZOS
print(f"指定滤波器缩放后尺寸: {img_resized_hq.size}")
img_resized_hq.save("photo_resized_hq.jpg")

except FileNotFoundError:
print(“请准备一个名为 photo.jpg 的图片文件进行测试缩放。”)
except Exception as e:
print(f”缩放操作时发生错误: {e}”)
“`

resize() 方法的第二个参数是可选的滤波器(filter)。在缩小图像时,使用高质量的滤波器(如 LANCZOSBICUBIC)可以减少锯齿和摩尔纹,但会增加计算时间。放大图像时,滤波器效果不太明显。

裁剪 (Cropping)

使用 image.crop() 方法可以从图像中提取一个矩形区域。它接受一个四元组 (left, upper, right, lower) 作为参数,表示裁剪区域的左上角和右下角坐标。坐标系统以图像左上角为原点 (0, 0),向右为 X 轴正方向,向下为 Y 轴正方向。

“`python
from PIL import Image

try:
img = Image.open(“photo.jpg”)
print(f”原始尺寸: {img.size}”)

# 裁剪出图像中心的一个 100x100 像素区域
width, height = img.size
left = (width - 100) // 2
top = (height - 100) // 2
right = (width + 100) // 2
bottom = (height + 100) // 2

img_cropped = img.crop((left, top, right, bottom))
print(f"裁剪后尺寸: {img_cropped.size}")
img_cropped.save("photo_cropped.jpg")

except FileNotFoundError:
print(“请准备一个名为 photo.jpg 的图片文件进行测试裁剪。”)
except Exception as e:
print(f”裁剪操作时发生错误: {e}”)
“`

crop() 方法也返回一个新的 Image 对象。

旋转 (Rotating)

使用 image.rotate() 方法可以旋转图像。它接受旋转角度(以度为单位,逆时针方向)作为参数。

“`python
from PIL import Image

try:
img = Image.open(“photo.jpg”)

# 逆时针旋转 90 度
img_rotated_90 = img.rotate(90)
img_rotated_90.save("photo_rotated_90.jpg")
print("图像逆时针旋转 90 度并保存。")

# 顺时针旋转 45 度 (即逆时针旋转 -45 度)
img_rotated_45 = img.rotate(-45)
img_rotated_45.save("photo_rotated_minus_45.jpg")
print("图像顺时针旋转 45 度并保存。")

# 旋转时,如果图像边缘会超出原始边界,可以通过 expand=True 参数扩展画布
img_rotated_expand = img.rotate(45, expand=True)
img_rotated_expand.save("photo_rotated_expand.jpg")
print("图像旋转 45 度并扩展画布保存。")

# 旋转时使用不同的填充颜色 (对于 expand=True 或非 90 度整数倍旋转)
img_rotated_fill = img.rotate(30, expand=True, fillcolor="lightblue")
img_rotated_fill.save("photo_rotated_fill.jpg")
print("图像旋转 30 度并扩展画布,用浅蓝色填充保存。")

except FileNotFoundError:
print(“请准备一个名为 photo.jpg 的图片文件进行测试旋转。”)
except Exception as e:
print(f”旋转操作时发生错误: {e}”)
“`

rotate() 方法返回一个新的 Image 对象。expand=True 参数在旋转非 90 度的整数倍时非常有用,可以避免图像内容被裁剪。fillcolor 参数用于指定扩展区域或旋转留下的空白区域的填充颜色。

翻转/镜像 (Flipping/Mirroring)

Pillow 的 ImageOps 模块提供了一些额外的图像操作,包括翻转和镜像。

“`python
from PIL import Image, ImageOps

try:
img = Image.open(“photo.jpg”)

# 水平镜像 (沿垂直轴翻转)
img_mirror = ImageOps.mirror(img)
img_mirror.save("photo_mirror.jpg")
print("图像水平镜像并保存。")

# 垂直翻转 (沿水平轴翻转)
img_flip = ImageOps.flip(img)
img_flip.save("photo_flip.jpg")
print("图像垂直翻转并保存。")

except FileNotFoundError:
print(“请准备一个名为 photo.jpg 的图片文件进行测试翻转。”)
except Exception as e:
print(f”翻转操作时发生错误: {e}”)
“`

ImageOps 模块中的函数通常直接对输入的 Image 对象进行操作,并返回修改后的新对象(或者在某些情况下是输入对象的修改版本,但为了代码清晰,通常认为是返回新对象)。

6. 色彩与增强:调整图像外观

Pillow 的 ImageEnhance 模块提供了一系列类,用于调整图像的亮度、对比度、色彩饱和度和锐度。

“`python
from PIL import Image, ImageEnhance

try:
img = Image.open(“photo.jpg”)

# 调整亮度
enhancer_brightness = ImageEnhance.Brightness(img)
# factor > 1 增加亮度,factor < 1 降低亮度
img_bright = enhancer_brightness.enhance(1.5) # 增加 50% 亮度
img_bright.save("photo_bright.jpg")
print("图像亮度调整并保存。")

# 调整对比度
enhancer_contrast = ImageEnhance.Contrast(img)
img_contrast = enhancer_contrast.enhance(0.8) # 降低 20% 对比度
img_contrast.save("photo_contrast.jpg")
print("图像对比度调整并保存。")

# 调整色彩饱和度
enhancer_color = ImageEnhance.Color(img)
img_color = enhancer_color.enhance(2.0) # 增加一倍饱和度
img_color.save("photo_color.jpg")
print("图像饱和度调整并保存。")

# 调整锐度
enhancer_sharpness = ImageEnhance.Sharpness(img)
img_sharp = enhancer_sharpness.enhance(3.0) # 增加锐度
img_sharp.save("photo_sharp.jpg")
print("图像锐度调整并保存。")

except FileNotFoundError:
print(“请准备一个名为 photo.jpg 的图片文件进行测试色彩调整。”)
except Exception as e:
print(f”色彩调整操作时发生错误: {e}”)
“`

使用方法是先创建一个对应增强类型的增强器对象,传入要处理的图像,然后调用增强器对象的 enhance() 方法,传入一个因子(factor)来控制增强程度。

7. 滤镜效果:为图像添加艺术感

Pillow 的 ImageFilter 模块包含了一些预定义的图像滤镜,可以用来实现模糊、锐化、边缘检测等效果。

“`python
from PIL import Image, ImageFilter

try:
img = Image.open(“photo.jpg”)

# 应用模糊滤镜
img_blurred = img.filter(ImageFilter.BLUR)
img_blurred.save("photo_blurred.jpg")
print("应用模糊滤镜并保存。")

# 应用边缘增强滤镜
img_edge_enhance = img.filter(ImageFilter.EDGE_ENHANCE)
img_edge_enhance.save("photo_edge_enhance.jpg")
print("应用边缘增强滤镜并保存。")

# 应用锐化滤镜
img_sharpened = img.filter(ImageFilter.SHARPEN)
img_sharpened.save("photo_sharpened.jpg")
print("应用锐化滤镜并保存。")

# 应用高斯模糊滤镜 (可以指定半径 radius)
img_gaussian_blur = img.filter(ImageFilter.GaussianBlur(radius=5))
img_gaussian_blur.save("photo_gaussian_blur.jpg")
print("应用高斯模糊滤镜并保存。")

except FileNotFoundError:
print(“请准备一个名为 photo.jpg 的图片文件进行测试滤镜。”)
except Exception as e:
print(f”滤镜操作时发生错误: {e}”)
“`

image.filter() 方法接受一个滤镜对象作为参数,并返回应用滤镜后的新图像对象。ImageFilter 模块提供了多种滤镜,如 BLUR, CONTOUR, DETAIL, EDGE_ENHANCE, EDGE_ENHANCE_MORE, EMBOSS, FIND_EDGES, SMOOTH, SMOOTH_MORE, SHARPEN, GaussianBlur, UnsharpMask 等。

8. 在图像上绘制:添加文字、图形和水印

Pillow 的 ImageDraw 模块提供了强大的 2D 绘图功能,你可以在图像上绘制点、线、矩形、圆形、文本等。这对于添加水印、标注图像或创建简单的图形非常有用。

绘图需要在图像对象上创建一个 Draw 对象,所有绘制操作都通过这个 Draw 对象进行。

“`python
from PIL import Image, ImageDraw, ImageFont

try:
# 打开一个图像,或者创建一个新图像
img = Image.new(‘RGB’, (400, 200), color = ‘white’) # 创建一个白色背景图

# 创建一个 Draw 对象
draw = ImageDraw.Draw(img)

# 绘制一些形状

# 绘制矩形 (左上角,右下角)
draw.rectangle([(50, 50), (150, 150)], outline="red", fill="yellow", width=3)

# 绘制圆形 (外切矩形左上角,外切矩形右下角)
draw.ellipse([(200, 50), (300, 150)], outline="blue", fill="green", width=3)

# 绘制直线 (起点,终点)
draw.line([(50, 180), (350, 180)], fill="black", width=5)

# 绘制文本
try:
    # 尝试加载一个字体文件 (比如 Arial.ttf 或 simsun.ttc)
    # 你需要确保系统中有这个字体文件或者提供完整的路径
    font = ImageFont.truetype("arial.ttf", 30) # Linux/macOS 系统通常有 arial.ttf 或 DejaVuSans.ttf
    # 对于 Windows 系统,可以尝试 "C:/Windows/Fonts/arial.ttf" 或 "simsun.ttc" 等
except IOError:
    # 如果字体文件找不到,使用默认字体
    font = ImageFont.load_default()
    print("未找到指定字体文件,使用默认字体。")

text = "Hello, Pillow!"
text_color = (0, 0, 0) # 黑色
text_position = (50, 10)

draw.text(text_position, text, fill=text_color, font=font)

# 保存绘制后的图像
img.save("drawing_example.png")
print("绘制示例图像并保存。")

# --- 为现有图像添加水印 ---
try:
    original_img = Image.open("photo.jpg")
    # 确保图像模式适合绘图 (如果原图是 L 或 P 模式,可能需要转换)
    if original_img.mode != 'RGB' and original_img.mode != 'RGBA':
         original_img = original_img.convert('RGBA') # 转换为支持透明度的模式

    draw_watermark = ImageDraw.Draw(original_img)

    watermark_text = "Watermark"
    watermark_font_size = 40
    try:
         watermark_font = ImageFont.truetype("arial.ttf", watermark_font_size)
    except IOError:
         watermark_font = ImageFont.load_default()
         print("未找到指定水印字体文件,使用默认字体。")

    # 获取文本尺寸,以便居中或定位
    # 在 Pillow 8.0+ 中,使用 textbbox 或 textlength
    try:
        bbox = draw_watermark.textbbox((0, 0), watermark_text, font=watermark_font)
        text_width = bbox[2] - bbox[0]
        text_height = bbox[3] - bbox[1]
    except AttributeError:
        # Pillow < 8.0 使用 textsize (已弃用)
        text_width, text_height = draw_watermark.textsize(watermark_text, font=watermark_font)


    img_width, img_height = original_img.size
    watermark_position = (img_width - text_width - 10, img_height - text_height - 10) # 放在右下角,留 10px 边距

    # 设置水印颜色 (可以带透明度,比如半透明灰色 (128, 128, 128, 128))
    watermark_color = (128, 128, 128, 128) # RGBA 模式下的半透明灰色

    draw_watermark.text(watermark_position, watermark_text, fill=watermark_color, font=watermark_font)

    original_img.save("photo_with_watermark.png") # 保存为 PNG 以保留透明度
    print("为图像添加水印并保存。")

except FileNotFoundError:
     print("请准备一个名为 photo.jpg 的图片文件进行测试水印。")

except Exception as e:
print(f”绘图或水印操作时发生错误: {e}”)

“`

ImageDraw.Draw() 接受一个图像对象作为参数。绘制文本时,可以使用 ImageFont.truetype() 加载 TrueType 字体文件,或者使用 ImageFont.load_default() 加载一个简单的位图字体。请注意,加载 TrueType 字体需要指定字体文件路径和字号。绘制带透明度的图形或文本(如水印)需要图像处于支持 Alpha 通道的模式,最常见的是 RGBA 模式。

9. 像素操作:深入图像细节

虽然 Pillow 的高级方法(如 resize, filter, enhance)通常效率更高,但在某些特定场景下,你可能需要直接访问或修改图像的单个像素。

获取像素值

使用 image.getpixel((x, y)) 方法可以获取指定坐标 (x, y) 处的像素值。返回值取决于图像模式。

“`python
from PIL import Image

try:
img = Image.open(“photo.jpg”) # 假设 photo.jpg 存在
width, height = img.size

# 获取左上角像素值
pixel_top_left = img.getpixel((0, 0))
print(f"左上角像素值: {pixel_top_left}")

# 获取中心像素值
pixel_center = img.getpixel((width // 2, height // 2))
print(f"中心像素值: {pixel_center}")

# 如果是 RGB 模式,返回值是 (R, G, B) 元组
# 如果是 RGBA 模式,返回值是 (R, G, B, A) 元组
# 如果是 L 模式,返回值是一个整数 (0-255)

except FileNotFoundError:
print(“请准备一个名为 photo.jpg 的图片文件进行测试像素操作。”)
except Exception as e:
print(f”获取像素操作时发生错误: {e}”)
“`

修改像素值

使用 image.putpixel((x, y), value) 方法可以设置指定坐标 (x, y) 处的像素值为 valuevalue 的类型和范围也取决于图像模式。

“`python
from PIL import Image

创建一个简单的 RGB 图像

img = Image.new(“RGB”, (100, 100), “blue”)

修改中心像素为红色

img.putpixel((50, 50), (255, 0, 0))

在图像上绘制一个斜对角线 (像素级别操作示例,效率较低)

width, height = img.size
for i in range(min(width, height)):
img.putpixel((i, i), (255, 255, 0)) # 设置为黄色

img.save(“pixel_edit_example.png”)
print(“像素编辑示例图像并保存。”)
“`

警告: 直接使用 getpixel()putpixel() 在 Python 循环中进行大量像素操作效率非常低下。对于需要处理大量像素的复杂操作(如遍历所有像素进行某种计算),强烈建议将图像转换为 NumPy 数组进行处理,然后再转换回 Pillow Image 对象。

10. 与 NumPy 集成:高效的像素处理

Pillow 可以轻松地与 NumPy 库进行转换。图像对象可以转换为 NumPy 数组,NumPy 数组也可以转换回图像对象。这使得你可以利用 NumPy 强大的数组操作能力进行高效的像素级计算,或与其他依赖 NumPy 的库(如 OpenCV、SciPy)协同工作。

Pillow Image 转换为 NumPy Array

“`python
from PIL import Image
import numpy as np

try:
img = Image.open(“photo.jpg”)
print(f”原始图像模式: {img.mode}, 尺寸: {img.size}”)

# 将 Pillow Image 转换为 NumPy 数组
# 如果图像是 RGB,数组形状将是 (height, width, 3)
# 如果图像是 L,数组形状将是 (height, width)
img_array = np.array(img)

print(f"转换为 NumPy 数组后的形状: {img_array.shape}")
print(f"数组数据类型: {img_array.dtype}")
# 例如,对于 RGB 图像,img_array[y, x] 是像素 (x, y) 的 (R, G, B) 元组

# 使用 NumPy 进行一些简单的像素操作 (例如,将所有像素值加 50)
# 注意:确保操作不会超出像素值的范围 (0-255)
# 可以使用 np.clip() 来限制范围
img_array_brightened = np.clip(img_array + 50, 0, 255).astype(np.uint8)
print("使用 NumPy 增加像素值。")

# 注意:如果图像是 RGBA,shape 将是 (height, width, 4)
# 如果是 L,shape 将是 (height, width)

except FileNotFoundError:
print(“请准备一个名为 photo.jpg 的图片文件进行测试 NumPy 转换。”)
except Exception as e:
print(f”转换为 NumPy 数组时发生错误: {e}”)
“`

NumPy Array 转换为 Pillow Image

“`python
from PIL import Image
import numpy as np

try:
# 创建一个 NumPy 数组 (例如,一个 100×200 的 RGB 随机图像)
# 注意数据类型通常是 np.uint8 (无符号 8 位整数)
random_array = np.random.randint(0, 256, size=(100, 200, 3), dtype=np.uint8)
print(f”原始 NumPy 数组形状: {random_array.shape}”)

# 将 NumPy 数组转换回 Pillow Image
# 参数:array, mode (可选,如果数组形状有歧义,如 (H, W) 可能是 L 或 P)
img_from_array = Image.fromarray(random_array, 'RGB') # 明确指定模式
print(f"从 NumPy 数组转换后的图像模式: {img_from_array.mode}, 尺寸: {img_from_array.size}")

img_from_array.save("image_from_array.png")
print("从 NumPy 数组创建图像并保存。")

# 结合之前的 NumPy 操作示例
# 假设 img_array_brightened 是经过 NumPy 操作的数组
img_processed_via_numpy = Image.fromarray(img_array_brightened) # mode 通常可以自动推断

img_processed_via_numpy.save("photo_numpy_brightened.jpg")
print("通过 NumPy 处理后转换回图像并保存。")

except NameError:
print(“请先运行前面的代码块生成 img_array_brightened 数组。”)
except Exception as e:
print(f”从 NumPy 数组转换时发生错误: {e}”)
“`

NumPy 集成为进行复杂的数学运算、数据分析或与深度学习框架(它们通常处理图像作为 NumPy 数组)集成提供了极大的便利。

11. 更多高级功能(简述)

Pillow 还提供了许多其他功能,包括:

  • EXIF 数据: 通过 PIL.ExifTags 模块可以读取和写入 JPEG 图像的 EXIF 数据(如相机型号、拍摄日期、地理位置等)。
  • 图像序列: 可以打开和保存多帧图像,如 GIF 动画。
  • 颜色空间转换: image.convert() 方法不仅可以改变像素深度(如 L 到 RGB),还可以进行颜色空间转换(如 RGB 到 CMYK)。
  • 直方图: image.histogram() 可以计算图像的直方图,这对于图像分析(如自动调整对比度)很有用。
  • ImageFile 模块: 提供了对文件对象进行操作的能力,而无需将整个文件加载到内存中,对于处理大文件很有帮助。

12. 性能考虑

在使用 Pillow 进行图像处理时,性能是一个需要考虑的问题。以下是一些提示:

  • 避免像素循环: 如前所述,尽量使用内置方法(resize, filter, enhance 等)或 NumPy 数组操作,而不是 Python 循环中的 getpixel()putpixel()
  • 选择合适的模式: 如果不需要彩色信息或透明度,将图像转换为 LRGB 模式可以减少数据量,有时会加快处理速度。
  • 延迟加载: Image.open() 在打开文件时默认是延迟加载的,它只读取文件头信息,实际像素数据在需要时才加载。如果需要立即加载所有数据,可以调用 image.load()
  • 优化保存: 对于 JPEG 格式,适当降低 quality 参数可以减小文件大小和保存时间(但会损失图像质量)。对于 PNG 格式,使用 optimize=True 可以减小文件大小。

13. 总结与展望

Pillow 作为 PIL 的现代继任者,已经成为 Python 图像处理的标准库。它易于安装和使用,提供了丰富的功能,可以满足绝大多数常见的图像处理需求。从简单的格式转换和几何变换,到复杂的滤镜应用和像素操作,Pillow 都提供了直观的 API。

通过本文的学习,你已经掌握了 Pillow 的核心概念和基本用法。但这仅仅是开始,Pillow 还有许多其他功能等待你去探索。查阅官方文档是进一步学习的最佳途径,它提供了更详细的函数说明和更多的示例。

在数据科学、机器学习、Web 开发、自动化脚本等众多领域,Pillow 都是一个非常有价值的工具。掌握它,将大大提升你在处理图像相关任务时的效率和能力。现在,拿起你的键盘,开始用 Python 和 Pillow 创造精彩的图像吧!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部