费曼学习法2 - 图像的 “变形计“:缩放、裁剪与旋转 (基础篇)

发布于:2025-02-21 ⋅ 阅读:(19) ⋅ 点赞:(0)

第二篇:图像的 “变形计”:缩放、裁剪与旋转 (基础篇)

开篇提问:

还记得小时候玩的哈哈镜吗? 它可以把我们拉长、压扁,或者扭曲成各种滑稽的形状,让我们哈哈大笑。 虽然 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)。

  1. 图像缩放 (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 // 2img.height // 2: 使用整除 // 来计算缩小一半后的宽度和高度,确保结果是整数像素值。
    • resized_img_xxx.save("resized_image_xxx.jpg"): 将缩放后的图像保存为新的 JPG 文件。

    动手练习:

    1. 修改代码:image_path = "your_image.jpg" 替换成你图片的实际路径。
    2. 运行代码: 保存代码为 .py 文件 (例如 resize_image.py),然后在终端中运行 python resize_image.py 命令。
    3. 查看效果: 查看生成的 resized_image_half.jpg, resized_image_double.jpg, resized_image_custom.jpg 文件,比较它们的尺寸变化。
  2. 图像裁剪 (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 作为参数,返回裁剪后的图像对象。

    动手练习:

    1. 修改代码:image_path = "your_image.jpg" 替换成你图片的实际路径,并尝试修改 crop_box 的值,裁剪不同的区域。
    2. 运行代码: 保存代码为 .py 文件 (例如 crop_image.py),然后在终端中运行 python crop_image.py 命令。
    3. 查看效果: 查看生成的 cropped_image.jpg 文件,观察裁剪效果。
  3. 图像旋转 (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 度的整数倍时,为了保持图像内容完整,旋转后的图像尺寸通常会比原始图像略大,并且会用背景色填充旋转产生的空白区域 (默认背景色是黑色)。

    动手练习:

    1. 修改代码:image_path = "your_image.jpg" 替换成你图片的实际路径,并尝试修改 rotate() 的角度值,观察旋转效果。
    2. 运行代码: 保存代码为 .py 文件 (例如 rotate_image.py),然后在终端中运行 python rotate_image.py 命令。
    3. 查看效果: 查看生成的 rotated_image_45.jpg, rotated_image_90.jpg, rotated_image_180.jpg 文件,观察旋转效果和尺寸变化。
  4. 图像翻转 (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 进行水平翻转。

    动手练习:

    1. 修改代码:image_path = "your_image.jpg" 替换成你图片的实际路径。
    2. 运行代码: 保存代码为 .py 文件 (例如 flip_mirror_image.py),然后在终端中运行 python flip_mirror_image.py 命令。
    3. 查看效果: 查看生成的 flipped_image.jpgmirrored_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
  2. 修改代码:input_folder = "avatars_input"output_folder = "avatars_output" 修改为你实际的文件夹路径 (如果需要)。
  3. 运行代码: 保存代码为 .py 文件 (例如 avatar_wall.py),然后在终端中运行 python avatar_wall.py 命令。
  4. 查看效果: 查看 avatars_output 文件夹,你会看到所有头像都被处理成了 128x128 像素的正方形头像,并且尽可能保持了原始比例。

费曼回顾 (知识巩固):

请你用自己的话,总结一下今天我们学习的图像 “变形” 操作,包括:

  • 图像缩放的原理和方法?
  • 图像裁剪的作用和裁剪框的定义?
  • 图像旋转的角度和旋转方向?
  • 图像翻转的类型 (水平和垂直)?
  • 在头像墙案例中,我们是如何综合运用缩放和裁剪来实现头像统一尺寸的?

像给你的朋友讲解一样,用最简单的语言解释这些概念,并举一些生活中的例子来帮助理解。

课后思考 (拓展延伸):

  1. 尝试修改头像墙案例的代码,实现以下功能:
    • 将头像处理成圆形头像 (提示:可以使用蒙版或透明通道)。
    • 将头像拼接成一个真正的 “墙” (例如,网格状排列)。
  2. 思考一下,除了头像处理,图像缩放、裁剪和旋转还可以应用在哪些场景中? 例如,网页设计、广告制作、视频编辑等等。
  3. 尝试探索 img.resize()img.rotate() 方法的其他参数,例如 resample (重采样滤波器) 和 expand (旋转后是否扩展图像尺寸) 等,看看它们有什么作用?

恭喜你!完成了 PIL 库的第二篇文章学习! 你已经掌握了图像 “变形” 的基础技能,可以开始 “玩转” 图像的尺寸、形状和方向了! 下一篇文章,我们将进入色彩的世界,探索图像的颜色模式和通道,让你的图像处理技能更上一层楼! 敬请期待!


网站公告

今日签到

点亮在社区的每一天
去签到