1. PIL.Image模块基础与安装
PIL(Python Imaging Library)是Python平台最常用的图像处理库之一,虽然原版PIL已停止维护,但其分支Pillow完全兼容PIL并持续更新。在实际项目中,我们通常直接安装Pillow来使用PIL的功能。
安装Pillow非常简单,只需在命令行中运行:
pip install pillowImage模块是Pillow中最核心的组件,它提供了一个同名类Image来表示图像对象。这个模块支持超过30种图像格式的读写操作,包括常见的JPEG、PNG、BMP等。我经常用它来处理网站图片预处理、数据集标准化等任务,实测下来非常稳定可靠。
先来看个最简单的例子,如何打开并显示一张图片:
from PIL import Image # 打开图片文件 img = Image.open('example.jpg') # 显示图片 img.show() # 获取图片基本信息 print(f"格式: {img.format}, 尺寸: {img.size}, 模式: {img.mode}")这里有几个关键属性需要注意:
format:图片格式(JPEG/PNG等)size:图片尺寸(宽度,高度)mode:色彩模式(RGB/L/CMYK等)
2. 图片尺寸调整基础方法
resize()是PIL.Image模块中最常用的尺寸调整方法,它的基本语法如下:
resized_img = img.resize((new_width, new_height), resample=Image.BICUBIC)这个方法接受两个主要参数:
size:包含新宽度和高度的元组resample:重采样滤波器,影响缩放质量
我做过对比测试,不同滤波器在速度和效果上有明显差异:
| 滤波器 | 质量 | 速度 | 适用场景 |
|---|---|---|---|
| NEAREST | 低 | 最快 | 像素风格处理 |
| BILINEAR | 中 | 快 | 普通缩放 |
| BICUBIC | 高 | 中等 | 高质量缩放(默认) |
| LANCZOS | 最高 | 慢 | 专业级处理 |
实际项目中,我通常这样使用:
from PIL import Image # 高质量缩小图片 img = Image.open('large_image.jpg') small_img = img.resize((800, 600), Image.LANCZOS) small_img.save('small_image.jpg', quality=95) # 快速放大图片(保持锐利边缘) big_img = img.resize((2000, 1500), Image.NEAREST)注意一个常见误区:resize()会返回新图像对象,不会修改原图。如果需要修改原图,需要重新赋值。
3. 保持宽高比的智能缩放
实际项目中,我们经常需要保持图片原始宽高比进行缩放。这需要先计算缩放比例,再应用resize。下面是我常用的两种方法:
方法一:固定宽度,高度自适应
def resize_with_aspect(img, target_width): width_percent = target_width / float(img.size[0]) target_height = int(float(img.size[1]) * float(width_percent)) return img.resize((target_width, target_height), Image.BICUBIC)方法二:限定最大尺寸(类似缩略图)
def resize_with_max(img, max_size): original_width, original_height = img.size ratio = min(max_size[0]/original_width, max_size[1]/original_height) new_size = (int(original_width*ratio), int(original_height*ratio)) return img.resize(new_size, Image.LANCZOS)我在电商项目中处理商品图片时,经常需要生成不同尺寸的版本。比如主图需要保持1:1比例,详情图需要保持3:4比例。这时可以结合裁剪和缩放:
def resize_and_crop(img, target_size, crop_position='center'): # 先缩放短边到目标尺寸 img_ratio = img.size[0] / img.size[1] target_ratio = target_size[0] / target_size[1] if img_ratio >= target_ratio: # 图片较宽,按高度缩放 new_height = target_size[1] new_width = int(new_height * img_ratio) else: # 图片较高,按宽度缩放 new_width = target_size[0] new_height = int(new_width / img_ratio) img = img.resize((new_width, new_height), Image.LANCZOS) # 计算裁剪区域 if crop_position == 'center': left = (new_width - target_size[0])/2 top = (new_height - target_size[1])/2 elif crop_position == 'top': left = (new_width - target_size[0])/2 top = 0 else: # 'bottom' left = (new_width - target_size[0])/2 top = new_height - target_size[1] right = left + target_size[0] bottom = top + target_size[1] return img.crop((left, top, right, bottom))4. 批量图片处理实战
处理单张图片相对简单,但实际项目中我们经常需要批量处理大量图片。下面分享我在实际工作中的几种批量处理方法。
4.1 基础批量处理
最简单的批量resize脚本:
import os from PIL import Image input_folder = 'input_images' output_folder = 'resized_images' target_size = (800, 600) if not os.path.exists(output_folder): os.makedirs(output_folder) for filename in os.listdir(input_folder): if filename.lower().endswith(('.png', '.jpg', '.jpeg')): try: with Image.open(os.path.join(input_folder, filename)) as img: img_resized = img.resize(target_size, Image.LANCZOS) output_path = os.path.join(output_folder, filename) img_resized.save(output_path, quality=95) print(f"处理完成: {filename}") except Exception as e: print(f"处理失败 {filename}: {str(e)}")4.2 多尺寸批量生成
电商项目经常需要生成多种尺寸的图片,这是我优化过的代码:
import os from PIL import Image from concurrent.futures import ThreadPoolExecutor def generate_sizes(img_path, output_dir, sizes): """生成多种尺寸的图片""" try: with Image.open(img_path) as img: base_name = os.path.splitext(os.path.basename(img_path))[0] for size in sizes: # 保持宽高比的缩放 img_resized = resize_with_max(img, size) output_path = os.path.join( output_dir, f"{base_name}_{size[0]}x{size[1]}.jpg" ) img_resized.save(output_path, quality=85, optimize=True) except Exception as e: print(f"Error processing {img_path}: {str(e)}") def batch_process(input_dir, output_base, size_config): """批量处理目录中的所有图片""" if not os.path.exists(output_base): os.makedirs(output_base) image_files = [ f for f in os.listdir(input_dir) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp')) ] # 使用线程池加速处理 with ThreadPoolExecutor(max_workers=4) as executor: for filename in image_files: img_path = os.path.join(input_dir, filename) output_dir = os.path.join(output_base, os.path.splitext(filename)[0]) if not os.path.exists(output_dir): os.makedirs(output_dir) executor.submit(generate_sizes, img_path, output_dir, size_config) # 配置需要生成的尺寸 SIZE_CONFIG = [ (1200, 1200), # 主图 (800, 800), # 列表图 (400, 400), # 缩略图 (200, 200) # 小图标 ] batch_process('product_images', 'output', SIZE_CONFIG)4.3 性能优化技巧
处理大量图片时,性能优化很重要。以下是我总结的几个实用技巧:
- 使用多线程/多进程:Python的
concurrent.futures模块可以轻松实现 - 内存优化:及时关闭文件句柄,使用
with语句管理资源 - 渐进式JPEG:保存时设置
progressive=True可以优化网页加载 - 质量平衡:找到文件大小和视觉质量的平衡点(通常quality=85是个好选择)
# 优化后的保存参数 img.save(output_path, quality=85, optimize=True, progressive=True, dpi=(72, 72))5. 高级技巧与常见问题
5.1 处理透明通道(PNG)
当处理带透明通道的PNG图片时,需要特别注意保留alpha通道:
def resize_png(img_path, output_path, new_size): with Image.open(img_path) as img: if img.mode in ('RGBA', 'LA'): # 创建白色背景 background = Image.new('RGB', img.size, (255, 255, 255)) background.paste(img, mask=img.split()[-1]) # 使用alpha通道作为mask img = background img_resized = img.resize(new_size, Image.LANCZOS) img_resized.save(output_path, quality=95)5.2 处理大图内存问题
处理超大图片时可能会遇到内存问题,可以使用Image.draft()方法先加载低分辨率版本:
def safe_resize_large_image(img_path, output_path, max_dimension=5000): with Image.open(img_path) as img: # 检查图片尺寸 if max(img.size) > max_dimension: # 先加载缩小版本 img.draft(img.mode, (max_dimension, max_dimension)) img.load() # 按draft设置重新加载 # 计算合适的缩放尺寸 ratio = min(max_dimension/img.size[0], max_dimension/img.size[1]) new_size = (int(img.size[0]*ratio), int(img.size[1]*ratio)) img_resized = img.resize(new_size, Image.LANCZOS) img_resized.save(output_path)5.3 常见问题解决
- 图片变形:确保计算新尺寸时保持原始宽高比
- 质量下降:使用高质量滤波器(LANCZOS/BICUBIC),避免多次缩放
- 处理速度慢:对于批量处理,考虑使用多线程或专业图像处理库
- 颜色变化:检查图片模式(RGB/CMYK),必要时先转换模式
我在处理一个摄影网站项目时,发现直接resize会导致图片明显变模糊。后来改用两步缩放法显著提升了质量:
def two_step_resize(img, target_size): # 第一步:快速缩小到中间尺寸 intermediate_size = (target_size[0]*2, target_size[1]*2) img = img.resize(intermediate_size, Image.BILINEAR) # 第二步:精细调整到目标尺寸 img = img.resize(target_size, Image.LANCZOS) return img