第二篇:图像的 “变形计”:缩放、裁剪与旋转 (基础篇)
开篇提问:
还记得小时候玩的哈哈镜吗? 它可以把我们拉长、压扁,或者扭曲成各种滑稽的形状,让我们哈哈大笑。 虽然 PIL 库没有哈哈镜那么夸张,但它也能像魔法一样,对图像进行各种 “变形” 操作,比如 放大缩小、剪裁掉不需要的部分、或者让图像 旋转 起来。 这些 “变形” 技巧在图像处理中可是非常实用的哦! 今天,我们就来学习如何用 PIL 库给图像做个 “变形计”!
核心概念讲解 (费曼式解释):
1. 图像缩放 (Resize): “让图像变大变小”
图像缩放就像调整照片的尺寸一样,可以把图像放大或者缩小。 在 PIL 库中,我们使用 `img.resize()` 方法来实现图像缩放。
想象一下,你有一张很棒的风景照,但是尺寸太大了,想把它缩小一点方便在朋友圈分享。 或者,你有一张很小的图标,想把它放大一点看得更清楚。 这时候,图像缩放就派上用场了!
`img.resize()` 方法就像一个 **“尺寸调整器”**,你只需要告诉它你想要的 **新的宽度和高度**,它就能帮你把图像缩放到指定尺寸。
2. 图像裁剪 (Crop): “剪掉图像不需要的部分”
图像裁剪就像用剪刀剪照片一样,可以剪掉图像边缘不需要的部分,只留下你感兴趣的区域。 在 PIL 库中,我们使用 `img.crop()` 方法来实现图像裁剪。
比如,你拍了一张合照,但是背景太杂乱,或者只想突出照片中的某个人物,就可以使用图像裁剪,把不需要的部分 "咔嚓" 一下剪掉,让画面更简洁、更聚焦。
`img.crop()` 方法就像一把 **“魔法剪刀”**,你需要告诉它你想要 **裁剪的区域**,它就能帮你把指定区域内的图像 "剪" 下来。 裁剪区域通常用一个 **矩形框** 来表示,你需要指定矩形框的 **左上角和右下角坐标**。
3. 图像旋转 (Rotate): “让图像转个方向”
图像旋转就是把图像按照一定的角度旋转。 在 PIL 库中,我们使用 `img.rotate()` 方法来实现图像旋转。
有时候,我们拍的照片可能是歪的,或者我们想让图像呈现出不同的视觉效果,就可以使用图像旋转。 比如,把图像旋转 90 度,就可以让横向的图像变成纵向的。
`img.rotate()` 方法就像一个 **“旋转罗盘”**,你只需要告诉它你想要 **旋转的角度** (顺时针为正,逆时针为负),它就能帮你把图像旋转到指定的角度。
4. 图像翻转 (Flip & Mirror): “照镜子和倒过来”
图像翻转包括水平翻转 (镜像) 和垂直翻转 (上下颠倒)。 PIL 库的 `ImageOps` 模块提供了 `ImageOps.flip()` (垂直翻转) 和 `ImageOps.mirror()` (水平翻转) 方法来实现图像翻转。
水平翻转就像照镜子一样,把图像左右颠倒过来。 垂直翻转就像把图像倒过来一样,把图像上下颠倒过来。 图像翻转可以用来创造一些特殊的视觉效果,或者纠正一些拍摄上的错误。
`ImageOps.flip()` 就像 **“垂直翻转器”**,把图像上下翻转。 `ImageOps.mirror()` 就像 **“水平镜像器”**,把图像左右翻转。
代码实战 (动手练习):
我们还是用上一篇文章用过的 your_image.jpg
图片来做实验。 请确保你已经安装了 PIL 库 (Pillow)。
图像缩放 (Resize)
from PIL import Image image_path = "your_image.jpg" img = Image.open(image_path) print("原始尺寸:", img.size) # 输出原始尺寸 # 1. 缩小到一半 (宽度和高度都缩小为原来的 0.5 倍) resized_img_half = img.resize((img.width // 2, img.height // 2)) # 使用 // 整除,避免出现小数 print("缩小一半后的尺寸:", resized_img_half.size) resized_img_half.save("resized_image_half.jpg") # 2. 放大到两倍 (宽度和高度都放大到原来的 2 倍) resized_img_double = img.resize((img.width * 2, img.height * 2)) print("放大两倍后的尺寸:", resized_img_double.size) resized_img_double.save("resized_image_double.jpg") # 3. 缩放到指定尺寸 (例如 200x200 像素) resized_img_custom = img.resize((200, 200)) print("缩放到 200x200 后的尺寸:", resized_img_custom.size) resized_img_custom.save("resized_image_custom.jpg") print("图像缩放完成,已保存为 resized_image_xxx.jpg")
代码解释:
img.resize((new_width, new_height))
:resize()
方法接收一个元组(new_width, new_height)
作为参数,表示缩放后的新尺寸。img.width // 2
和img.height // 2
: 使用整除//
来计算缩小一半后的宽度和高度,确保结果是整数像素值。resized_img_xxx.save("resized_image_xxx.jpg")
: 将缩放后的图像保存为新的 JPG 文件。
动手练习:
- 修改代码: 将
image_path = "your_image.jpg"
替换成你图片的实际路径。 - 运行代码: 保存代码为
.py
文件 (例如resize_image.py
),然后在终端中运行python resize_image.py
命令。 - 查看效果: 查看生成的
resized_image_half.jpg
,resized_image_double.jpg
,resized_image_custom.jpg
文件,比较它们的尺寸变化。
图像裁剪 (Crop)
from PIL import Image image_path = "your_image.jpg" img = Image.open(image_path) print("原始尺寸:", img.size) # 定义裁剪区域 (左上角 x, 左上角 y, 右下角 x, 右下角 y) # 例如,裁剪图像中心区域 200x200 crop_box = ( img.width // 2 - 100, # 左上角 x 坐标 (中心点左移 100 像素) img.height // 2 - 100, # 左上角 y 坐标 (中心点上移 100 像素) img.width // 2 + 100, # 右下角 x 坐标 (中心点右移 100 像素) img.height // 2 + 100 # 右下角 y 坐标 (中心点下移 100 像素) ) cropped_img = img.crop(crop_box) print("裁剪后的尺寸:", cropped_img.size) cropped_img.save("cropped_image.jpg") print("图像裁剪完成,已保存为 cropped_image.jpg")
代码解释:
crop_box = (left, upper, right, lower)
:crop_box
是一个元组,定义了裁剪区域的矩形框。left
,upper
是左上角坐标,right
,lower
是右下角坐标。 注意坐标原点 (0, 0) 在图像的左上角,x 轴向右,y 轴向下。img.crop(crop_box)
:crop()
方法接收crop_box
作为参数,返回裁剪后的图像对象。
动手练习:
- 修改代码: 将
image_path = "your_image.jpg"
替换成你图片的实际路径,并尝试修改crop_box
的值,裁剪不同的区域。 - 运行代码: 保存代码为
.py
文件 (例如crop_image.py
),然后在终端中运行python crop_image.py
命令。 - 查看效果: 查看生成的
cropped_image.jpg
文件,观察裁剪效果。
图像旋转 (Rotate)
from PIL import Image image_path = "your_image.jpg" img = Image.open(image_path) print("原始尺寸:", img.size) # 1. 顺时针旋转 45 度 rotated_img_45 = img.rotate(45) # 默认逆时针旋转,所以正数是逆时针,这里要顺时针需要用负数或者 specify counterclock=False in PIL versions >= 10.0.0 print("旋转 45 度后的尺寸:", rotated_img_45.size) # 旋转后尺寸可能会改变 rotated_img_45.save("rotated_image_45.jpg") # 2. 逆时针旋转 90 度 (或者顺时针旋转 -90 度) rotated_img_90 = img.rotate(-90) # 或者 rotated_img_90 = img.rotate(90, counter_clockwise=False) print("旋转 90 度后的尺寸:", rotated_img_90.size) rotated_img_90.save("rotated_image_90.jpg") # 3. 旋转 180 度 rotated_img_180 = img.rotate(180) print("旋转 180 度后的尺寸:", rotated_img_180.size) rotated_img_180.save("rotated_image_180.jpg") print("图像旋转完成,已保存为 rotated_image_xxx.jpg")
代码解释:
img.rotate(angle)
:rotate()
方法接收一个angle
参数,表示旋转角度, 单位是度 (degrees)。 默认情况下,rotate()
是逆时针旋转。 在PIL库 10.0.0 版本之后,可以通过counterclock=False
参数来指定顺时针旋转。 在老版本中,正角度值是逆时针旋转,负角度值是顺时针旋转。- 旋转后图像尺寸可能会改变: 当旋转角度不是 90 度的整数倍时,为了保持图像内容完整,旋转后的图像尺寸通常会比原始图像略大,并且会用背景色填充旋转产生的空白区域 (默认背景色是黑色)。
动手练习:
- 修改代码: 将
image_path = "your_image.jpg"
替换成你图片的实际路径,并尝试修改rotate()
的角度值,观察旋转效果。 - 运行代码: 保存代码为
.py
文件 (例如rotate_image.py
),然后在终端中运行python rotate_image.py
命令。 - 查看效果: 查看生成的
rotated_image_45.jpg
,rotated_image_90.jpg
,rotated_image_180.jpg
文件,观察旋转效果和尺寸变化。
图像翻转 (Flip & Mirror)
from PIL import Image, ImageOps image_path = "your_image.jpg" img = Image.open(image_path) print("原始尺寸:", img.size) # 1. 垂直翻转 (上下颠倒) flipped_img = ImageOps.flip(img) print("垂直翻转后的尺寸:", flipped_img.size) flipped_img.save("flipped_image.jpg") # 2. 水平翻转 (左右镜像) mirrored_img = ImageOps.mirror(img) print("水平翻转后的尺寸:", mirrored_img.size) mirrored_img.save("mirrored_image.jpg") print("图像翻转完成,已保存为 flipped_image.jpg 和 mirrored_image.jpg")
代码解释:
from PIL import Image, ImageOps
: 需要从PIL
库中导入ImageOps
模块。ImageOps.flip(img)
: 对图像img
进行垂直翻转。ImageOps.mirror(img)
: 对图像img
进行水平翻转。
动手练习:
- 修改代码: 将
image_path = "your_image.jpg"
替换成你图片的实际路径。 - 运行代码: 保存代码为
.py
文件 (例如flip_mirror_image.py
),然后在终端中运行python flip_mirror_image.py
命令。 - 查看效果: 查看生成的
flipped_image.jpg
和mirrored_image.jpg
文件,观察翻转效果。
案例应用:制作头像墙,将不同尺寸的头像统一处理成固定尺寸
假设你有很多朋友的头像照片,尺寸大小不一,你想把它们统一处理成 128x128 像素的正方形头像,并拼接成一个头像墙。 我们可以使用图像缩放和裁剪来实现这个功能。
import os
from PIL import Image
input_folder = "avatars_input" # 输入文件夹路径 (包含各种尺寸的头像图片)
output_folder = "avatars_output" # 输出文件夹路径 (保存 128x128 像素的头像)
avatar_size = (128, 128) # 目标头像尺寸
# 确保输出文件夹存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
for filename in os.listdir(input_folder):
if filename.lower().endswith((".jpg", ".jpeg", ".png", ".bmp")): # 处理常见的图片格式
input_path = os.path.join(input_folder, filename)
output_path = os.path.join(output_folder, filename) # 输出文件名与输入文件名相同
try:
img = Image.open(input_path)
# 1. 缩放到目标尺寸 (可能会导致图像变形)
# resized_avatar = img.resize(avatar_size)
# 2. 更好的方法:先缩放到较小的尺寸,再裁剪中心区域,保持图像比例
# 计算缩放比例 (以较短边为基准缩放到目标尺寸的短边)
ratio = min(avatar_size[0] / img.width, avatar_size[1] / img.height)
resized_width = int(img.width * ratio)
resized_height = int(img.height * ratio)
resized_img = img.resize((resized_width, resized_height))
# 计算裁剪框,裁剪中心区域为目标尺寸
crop_box = (
(resized_width - avatar_size[0]) // 2,
(resized_height - avatar_size[1]) // 2,
(resized_width + avatar_size[0]) // 2,
(resized_height + avatar_size[1]) // 2
)
cropped_avatar = resized_img.crop(crop_box)
cropped_avatar.save(output_path)
print(f"已处理 {filename},并保存到 {output_path}")
except Exception as e:
print(f"处理 {filename} 出错: {e}")
print("头像处理完成!")
代码解释:
avatar_size = (128, 128)
: 定义目标头像尺寸为 128x128 像素。- 两种缩放方法:
- 方法 1 (注释掉的代码): 直接使用
img.resize(avatar_size)
缩放到目标尺寸,简单粗暴,但可能会导致图像比例失调,图像变形。 - 方法 2 (当前代码): 先计算缩放比例,将图像缩放到一个 较小的尺寸,使得图像的 较短边 与目标尺寸的 短边 相等 (或者更小)。 然后再从缩放后的图像中 裁剪中心区域,得到目标尺寸的头像。 这种方法可以尽可能保持图像的原始比例,避免图像变形。
- 方法 1 (注释掉的代码): 直接使用
- 计算裁剪框: 根据缩放后的尺寸和目标尺寸,计算裁剪框的坐标,确保裁剪的是图像的中心区域。
动手练习:
- 准备头像文件夹: 创建一个文件夹
avatars_input
,并将一些不同尺寸的头像图片 (jpg, png, bmp 等格式) 复制到这个文件夹中。 再创建一个空文件夹avatars_output
。 - 修改代码: 将
input_folder = "avatars_input"
和output_folder = "avatars_output"
修改为你实际的文件夹路径 (如果需要)。 - 运行代码: 保存代码为
.py
文件 (例如avatar_wall.py
),然后在终端中运行python avatar_wall.py
命令。 - 查看效果: 查看
avatars_output
文件夹,你会看到所有头像都被处理成了 128x128 像素的正方形头像,并且尽可能保持了原始比例。
费曼回顾 (知识巩固):
请你用自己的话,总结一下今天我们学习的图像 “变形” 操作,包括:
- 图像缩放的原理和方法?
- 图像裁剪的作用和裁剪框的定义?
- 图像旋转的角度和旋转方向?
- 图像翻转的类型 (水平和垂直)?
- 在头像墙案例中,我们是如何综合运用缩放和裁剪来实现头像统一尺寸的?
像给你的朋友讲解一样,用最简单的语言解释这些概念,并举一些生活中的例子来帮助理解。
课后思考 (拓展延伸):
- 尝试修改头像墙案例的代码,实现以下功能:
- 将头像处理成圆形头像 (提示:可以使用蒙版或透明通道)。
- 将头像拼接成一个真正的 “墙” (例如,网格状排列)。
- 思考一下,除了头像处理,图像缩放、裁剪和旋转还可以应用在哪些场景中? 例如,网页设计、广告制作、视频编辑等等。
- 尝试探索
img.resize()
和img.rotate()
方法的其他参数,例如resample
(重采样滤波器) 和expand
(旋转后是否扩展图像尺寸) 等,看看它们有什么作用?
恭喜你!完成了 PIL 库的第二篇文章学习! 你已经掌握了图像 “变形” 的基础技能,可以开始 “玩转” 图像的尺寸、形状和方向了! 下一篇文章,我们将进入色彩的世界,探索图像的颜色模式和通道,让你的图像处理技能更上一层楼! 敬请期待!