Python使用Pillow库修改图片尺寸的基础教程
本教程详细讲解如何使用Python的Pillow(PIL)库修改图片尺寸。涵盖Image.resize与Image.thumbnail的核心区别、等比例缩放计算、重采样滤镜选择及常见避坑指南。
在Python中批量处理或调整图片大小时,最稳定、功能最完备的工具是 Pillow(它是 Python Imaging Library 即 PIL 的现代化分支)。修改图片尺寸的核心操作主要依赖两个方法:生成新尺寸副本的 resize(),以及原地生成等比例缩略图的 thumbnail()。本文将从库的安装导入开始,系统讲解这两种方法的使用场景、等比例缩放数学计算、重采样滤镜的选取,以及日常开发中容易忽视的格式与通道避坑点。
准备工作:安装与读取图片
Pillow 可以在终端或命令行环境中使用 pip 直接安装:
pip install Pillow
安装完成后,在 Python 代码中通过导入 PIL 命名空间下的 Image 模块进行操作。读取本地图片并获取其元数据信息如下:
from PIL import Image
# 1. 打开指定路径下的图片文件
img = Image.open("photo.jpg")
# 2. 查看基本信息:size 返回 (宽度, 高度) 元组,单位为像素
orig_width, orig_height = img.size
print(f"原始尺寸: 宽 {orig_width}px, 高 {orig_height}px")
print(f"色彩模式: {img.mode}") # 例如: RGB, RGBA, L 等
print(f"图片格式: {img.format}") # 例如: JPEG, PNG 等
注意,Image.open() 采用懒加载机制,调用该函数时并不会立刻将全部像素数据解压到内存中,只有在进行尺寸修改、滤镜处理或保存时才会真正解码图片。
方法一:使用 resize() 调整为指定尺寸
resize() 是最通用的缩放方法,接收一个包含 (新宽度, 新高度) 的元组作为第一个参数,并返回一个新的 Image 实例。原本的图像对象保持不变。
1. 直接指定固定宽高(强制拉伸)
如果直接传入固定的长宽尺寸,当目标宽高比与原图不一致时,画面会被拉伸或压缩导致失真:
# 强制将图片尺寸修改为 400x300(若原图不是4:3比例,图像会发生形变)
resized_img = img.resize((400, 300))
resized_img.save("output_fixed.jpg")
2. 保持宽高比的手动计算缩放
在日常业务场景中,绝大多数情况下需要保持图片比例。我们可以根据目标宽度(或目标高度)推算对应维度的像素值:
# 设定目标宽度为 600px,高度自动按比例等比缩放
target_width = 600
orig_w, orig_h = img.size
# 计算缩放比例并确定目标高度
scale = target_width / orig_w
target_height = int(orig_h * scale)
# 使用等比尺寸生成新图
proportional_img = img.resize((target_width, target_height))
proportional_img.save("output_proportional.jpg")
print(f"调整后尺寸: {proportional_img.size}")
方法二:使用 thumbnail() 制作等比例缩略图
如果你只需要将一张高清大图缩小为指定边界内的缩略图,并不希望自行编写数学公式推算长宽,thumbnail() 是更优雅便捷的选择。
thumbnail() 接收一个 (最大宽度, 最大高度) 的元组。它的执行逻辑是:在保证图片宽高比不改变的前提下,将图片等比例缩小至完全容纳在给定的宽高矩形框内。更重要的是,该方法属于原地修改(In-place),直接改变当前对象自身,函数返回值为 None。
# 将图片约束在 800x800 的矩形框内
# 原图若为 1920x1080 (16:9),处理后实际变为 800x450
img.thumbnail((800, 800))
# 注意:切勿写成 img = img.thumbnail(...),否则 img 将变成 None
img.save("output_thumb.jpg")
print(f"缩略图最终尺寸: {img.size}")
- 返回值差异:
resize()返回处理后的新Image副本,原图对象保持不变;而thumbnail()原地改变原对象,返回值恒为None。 - 比例保护机制:
resize()严格按照传入尺寸拉伸;thumbnail()自动维持原纵横比,绝不变形。 - 尺寸放大处理:
resize()既可以放大也可以缩小;而thumbnail()默认只执行缩小操作,如果传入的尺寸大于原图尺寸,图片尺寸将保持原样不变。
重采样滤波器(Resampling Filter)的原理与选用
无论放大还是缩小图片,其本质都是重新计算像素矩阵中的颜色取值。如果直接丢弃或复制像素,容易造成严重的锯齿或噪点。Pillow 提供了多种重采样滤镜(Resampling Filter),通过 resample 参数进行配置。
在 Pillow 9.0.0 及以上版本中,建议使用现代枚举语法 Image.Resampling.<FILTER>(兼容旧版中形如 Image.LANCZOS 的常量):
# 使用最高品质的 LANCZOS 滤镜进行重采样缩放
high_quality_img = img.resize(
(target_width, target_height),
resample=Image.Resampling.LANCZOS
)
| 算法常量 | 运算速度 | 成像素质 | 核心原理与典型适用场景 |
|---|---|---|---|
Resampling.NEAREST |
极快 | 低(有锯齿) | 最近邻插值,直接复制最近的单个像素。适合像素画(Pixel Art)或需要保留绝对锐利硬边缘的二值化遮罩。 |
Resampling.BILINEAR |
快 | 中等 | 双线性插值,基于周围 2×2 像素进行线性加权。适合对处理性能敏感的中等缩放场景。 |
Resampling.BICUBIC |
中等 | 良好 | 双三次插值,基于周围 4×4 像素进行三次多项式平滑。常用于照片的平滑放大。 |
Resampling.LANCZOS |
稍慢 | 极高 | 基于 Sinc 函数的高阶滤波算法,在保留细微边缘锐利度的同时抑制混叠。常规图片缩小的首选算法。 |
常见问题与避坑指南
1. RGBA 透明通道保存为 JPEG 格式报错
如果原始图片是带有透明通道的 PNG 格式(其 mode 为 "RGBA"),直接缩放后保存为 .jpg 文件会抛出如下经典异常:
OSError: cannot write mode RGBA as JPEG
原因与解决方案: JPEG 格式标准本身不支持 Alpha 透明通道。在保存为 JPEG 之前,必须先将色彩空间转换成 RGB 模式。如果直接调用 convert('RGB'),透明区域可能会默认变成纯黑色。推荐新建白色背景图层进行粘贴合并:
def safe_convert_to_rgb(image):
if image.mode in ("RGBA", "LA") or (image.mode == "P" and "transparency" in image.info):
# 创建同等尺寸的纯白色 RGB 背景
background = Image.new("RGB", image.size, (255, 255, 255))
# 使用原图的 Alpha 通道作为遮罩粘贴
background.paste(image, mask=image.split()[3])
return background
return image.convert("RGB")
2. 手机拍摄照片缩放后发生“倒立”或“横置”
智能手机在拍照时通常不会真正物理旋转传感器捕获的图像,而是将手机朝向信息记录在图片文件的 EXIF 元数据中。Pillow 打开图片时默认不会自动执行 EXIF 旋转修正。使用 ImageOps.exif_transpose() 可以在缩放前自动恢复正确的视觉方向:
from PIL import ImageOps
# 依据 EXIF 标签自动转正图片,避免修改尺寸后方向错乱
img = ImageOps.exif_transpose(img)
3. thumbnail() 原地修改误赋值为 None
许多初学者习惯了 s = s.strip() 这种函数式赋值风格,误写为 img = img.thumbnail(...)。因为 thumbnail 是原地修改且返回 None,这会导致 img 变成空对象,紧接着执行 img.save() 时触发 AttributeError: 'NoneType' object has no attribute 'save'。
实用综合示例:构建通用的图片尺寸调整函数
将上述所有知识点(EXIF 方向校正、等比计算、透明通道保底兼容、高质量 Lanczos 重采样)整合为一个可以直接在实际项目中复用的通用函数:
import os
from PIL import Image, ImageOps
def resize_image_properly(input_path, output_path, max_width=800, quality=85):
"""
读取图片并将其等比例缩放至不超过 max_width,支持全格式兼容。
"""
if not os.path.exists(input_path):
raise FileNotFoundError(f"输入文件未找到: {input_path}")
# 1. 打开图片并自动校正拍摄朝向
with Image.open(input_path) as im:
im = ImageOps.exif_transpose(im)
orig_w, orig_h = im.size
# 2. 如果宽度已小于等于限制,无需放大(保留原尺寸)
if orig_w <= max_width:
target_w, target_h = orig_w, orig_h
else:
target_w = max_width
target_h = int(orig_h * (max_width / orig_w))
# 3. 使用 LANCZOS 高质量算法重采样
resized_im = im.resize((target_w, target_h), resample=Image.Resampling.LANCZOS)
# 4. 判断目标格式兼容性(若存为 JPEG 则处理 Alpha 通道)
ext = os.path.splitext(output_path)[1].lower()
if ext in [".jpg", ".jpeg"]:
if resized_im.mode in ("RGBA", "LA"):
bg = Image.new("RGB", resized_im.size, (255, 255, 255))
bg.paste(resized_im, mask=resized_im.split()[-1])
resized_im = bg
elif resized_im.mode != "RGB":
resized_im = resized_im.convert("RGB")
resized_im.save(output_path, "JPEG", quality=quality, optimize=True)
else:
# PNG 或 WebP 等格式可直接保存
resized_im.save(output_path, optimize=True)
print(f"处理完成: {output_path} ({target_w}x{target_h})")
# 示例调用
# resize_image_properly("avatar.png", "avatar_small.jpg", max_width=500)
结语
在 Python 中修改图片尺寸是一项基础而高频的任务。掌握 resize() 的副本生成机制与 thumbnail() 的就地修改逻辑,结合 Resampling.LANCZOS 重采样滤镜,能够确保图片在缩放过程中保持细腻清晰的质感。同时,在生产环境中务必注意通过 exif_transpose() 纠正拍摄姿态,以及妥善处理 RGBA 透明通道与 JPEG 格式之间的转换兼容,写出稳健可靠的图像批处理脚本。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













