Python 实现 等比例缩放图像到指定像素+填充
flyfish
代码
python
import os
from PIL import Image
from pathlib import Path
# ====================== 配置参数(可自行修改)======================
# 输入根目录(图片文件夹)
INPUT_ROOT = r"./input"
# 输出根目录(处理后的图片会保存在这里)
OUTPUT_ROOT = r"./output"
# 目标最大尺寸
TARGET_WIDTH = 1280
TARGET_HEIGHT = 720
# 填充颜色 (R, G, B)
FILL_COLOR = (114, 114, 114)
# 支持的图片格式(大小写都支持)
SUPPORT_FORMATS = (".jpg", ".JPG")
# =================================================================
def process_single_image(img_path: str, save_path: str):
"""
处理单张图片:转换格式 + 等比例缩放 + 居中填充
"""
try:
# 1. 打开图片,自动处理各种格式异常
with Image.open(img_path) as img:
# 2. 统一转为 RGB 格式(解决灰度图、透明通道报错)
if img.mode in ("RGBA", "LA"):
# 透明图:先创建纯色背景,再粘贴图片
background = Image.new("RGB", img.size, FILL_COLOR)
background.paste(img, mask=img.split()[-1])
img = background
elif img.mode != "RGB":
# 灰度图等其他格式直接转RGB
img = img.convert("RGB")
# 3. 等比例缩放计算
orig_w, orig_h = img.size
scale = min(TARGET_WIDTH / orig_w, TARGET_HEIGHT / orig_h)
new_w = int(orig_w * scale)
new_h = int(orig_h * scale)
# 高质量缩放
img_resized = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
# 4. 创建目标尺寸画布,居中放置图片
canvas = Image.new("RGB", (TARGET_WIDTH, TARGET_HEIGHT), FILL_COLOR)
offset_x = (TARGET_WIDTH - new_w) // 2
offset_y = (TARGET_HEIGHT - new_h) // 2
canvas.paste(img_resized, (offset_x, offset_y))
# 5. 保存图片(质量95)
canvas.save(save_path, "JPEG", quality=95)
print(f"处理完成: {save_path}")
except Exception as e:
print(f"处理失败: {img_path} | 错误信息: {str(e)}")
def batch_process_images():
"""
批量遍历文件夹,复刻目录结构,处理所有图片
"""
# 创建输出根目录
os.makedirs(OUTPUT_ROOT, exist_ok=True)
# 遍历所有子文件夹
for root, dirs, files in os.walk(INPUT_ROOT):
# 复刻目录结构到输出文件夹
relative_path = Path(root).relative_to(INPUT_ROOT)
output_dir = os.path.join(OUTPUT_ROOT, relative_path)
os.makedirs(output_dir, exist_ok=True)
# 遍历当前文件夹所有文件
for file in files:
if file.endswith(SUPPORT_FORMATS):
# 原始图片路径
img_input_path = os.path.join(root, file)
# 输出图片路径
img_output_path = os.path.join(output_dir, file)
# 处理单张图片
process_single_image(img_input_path, img_output_path)
if __name__ == "__main__":
print("=" * 60)
print("开始批量处理图片,最大尺寸:{}x{},填充颜色:{}".format(TARGET_WIDTH, TARGET_HEIGHT, FILL_COLOR))
print("=" * 60)
batch_process_images()
print("\n所有图片处理完成!")
可自定义参数
可以直接修改代码顶部的配置:
python
INPUT_ROOT = r"./input" # 输入文件夹
OUTPUT_ROOT = r"./output" # 输出文件夹
TARGET_WIDTH = 1280 # 目标宽度
TARGET_HEIGHT = 720 # 目标高度
FILL_COLOR = (114,114,114) # 填充颜色
等比例约束缩放,目标是:保证图片不变形的前提下,缩放后宽不超过 1280、高不超过 720,且图片尺寸尽可能大
逻辑
第 1 步:分别计算两个方向的缩放倍数
先拿目标尺寸和原图尺寸做除法,算出:
宽度方向:如果想让图片宽度刚好等于 1280,需要把原图放大/缩小多少倍
高度方向:如果想让图片高度刚好等于 720,需要把原图放大/缩小多少倍
公式:
宽度缩放比 = 目标宽度 / 原图宽度 = 1280 / 原图宽
高度缩放比 = 目标高度 / 原图高度 = 720 / 原图高
第 2 步:取两个倍数里更小的那个作为最终缩放比例
这是最关键的一步:选更小的缩放比,才能保证缩放后宽和高都不超出目标尺寸 。
如果选大的倍数,必然会有一个方向超过 1280 或 720,不符合"最大尺寸"的约束。
选小的倍数,会有一个方向刚好"顶到"目标边界,另一个方向小于目标尺寸,后续用填充补齐。
公式:
最终缩放比 = min(宽度缩放比, 高度缩放比)
第 3 步:计算缩放后的实际图片尺寸
用最终缩放比同时乘以原图宽高,保证宽高比和原图完全一致,不会拉伸变形。
公式:
缩放后宽度 = 原图宽度 × 最终缩放比
缩放后高度 = 原图高度 × 最终缩放比
第 4 步:居中放置 + 背景填充
创建一张 1280×720 的纯色画布,把缩放后的图片放在正中间,剩下的区域用填充色(114,114,114)填满。
举例子
例子 1:宽图(横版长图)
原图尺寸:2000 × 1000(宽高比 2:1)
- 宽度缩放比 = 1280 / 2000 = 0.64
- 高度缩放比 = 720 / 1000 = 0.72
- 取更小值:最终缩放比 = 0.64
- 缩放后尺寸:2000×0.64 = 1280 宽,1000×0.64 = 640 高
- 结果:宽度刚好顶满 1280,高度 640 < 720,上下用灰色填充
例子 2:竖图(高图)
原图尺寸:800 × 1200(宽高比 2:3)
- 宽度缩放比 = 1280 / 800 = 1.6
- 高度缩放比 = 720 / 1200 = 0.6
- 取更小值:最终缩放比 = 0.6
- 缩放后尺寸:800×0.6 = 480 宽,1200×0.6 = 720 高
- 结果:高度刚好顶满 720,宽度 480 < 1280,左右用灰色填充
例子 3:小图放大(你的需求场景)
原图尺寸:400 × 300(小图,宽高比 4:3)
- 宽度缩放比 = 1280 / 400 = 3.2
- 高度缩放比 = 720 / 300 = 2.4
- 取更小值:最终缩放比 = 2.4
- 缩放后尺寸:400×2.4 = 960 宽,300×2.4 = 720 高
- 结果:高度放大到 720 顶满,宽度 960 < 1280,左右用灰色填充
对应代码里的实现
python
# 第1步:拿到原图宽高
orig_w, orig_h = img.size
# 第2步:算两个方向的缩放比,取最小值
scale = min(TARGET_WIDTH / orig_w, TARGET_HEIGHT / orig_h)
# 第3步:计算缩放后的实际尺寸
new_w = int(orig_w * scale)
new_h = int(orig_h * scale)
# 执行高质量缩放
img_resized = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
# 第4步:创建1280×720画布,居中粘贴图片
canvas = Image.new("RGB", (TARGET_WIDTH, TARGET_HEIGHT), FILL_COLOR)
offset_x = (TARGET_WIDTH - new_w) // 2 # 水平居中偏移
offset_y = (TARGET_HEIGHT - new_h) // 2 # 垂直居中偏移
canvas.paste(img_resized, (offset_x, offset_y))