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)。在缩小图像时,使用高质量的滤波器(如 LANCZOS
或 BICUBIC
)可以减少锯齿和摩尔纹,但会增加计算时间。放大图像时,滤波器效果不太明显。
裁剪 (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)
处的像素值为 value
。value
的类型和范围也取决于图像模式。
“`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()
。 - 选择合适的模式: 如果不需要彩色信息或透明度,将图像转换为
L
或RGB
模式可以减少数据量,有时会加快处理速度。 - 延迟加载:
Image.open()
在打开文件时默认是延迟加载的,它只读取文件头信息,实际像素数据在需要时才加载。如果需要立即加载所有数据,可以调用image.load()
。 - 优化保存: 对于 JPEG 格式,适当降低
quality
参数可以减小文件大小和保存时间(但会损失图像质量)。对于 PNG 格式,使用optimize=True
可以减小文件大小。
13. 总结与展望
Pillow 作为 PIL 的现代继任者,已经成为 Python 图像处理的标准库。它易于安装和使用,提供了丰富的功能,可以满足绝大多数常见的图像处理需求。从简单的格式转换和几何变换,到复杂的滤镜应用和像素操作,Pillow 都提供了直观的 API。
通过本文的学习,你已经掌握了 Pillow 的核心概念和基本用法。但这仅仅是开始,Pillow 还有许多其他功能等待你去探索。查阅官方文档是进一步学习的最佳途径,它提供了更详细的函数说明和更多的示例。
在数据科学、机器学习、Web 开发、自动化脚本等众多领域,Pillow 都是一个非常有价值的工具。掌握它,将大大提升你在处理图像相关任务时的效率和能力。现在,拿起你的键盘,开始用 Python 和 Pillow 创造精彩的图像吧!