Python/PyTorch下如何将不等尺寸图像调整为固定大小并保持宽高比
保持宽高比输出固定尺寸图像的实现方案
要在不拉伸变形的前提下输出严格固定尺寸的图像,核心逻辑是等比缩放+边缘填充(Letterbox):先按比例缩放图像,让图像完整适配目标尺寸的边界,再把缩放后不足的边缘区域用固定颜色填充,最终输出严格符合要求的尺寸。你之前用的方法只做了缩放步骤,没有填充逻辑,自然没法在原图尺寸偏小/宽高比和目标不匹配时输出固定尺寸。
方案1:纯PIL实现(无深度学习框架依赖)
这个方案兼容所有尺寸的输入,不管原图比目标尺寸大还是小都能正常输出,不会出现拉伸、尺寸不达标的问题:
from PIL import Image def resize_keep_aspect(img, target_size=(128, 128), pad_color=(0, 0, 0)): origin_w, origin_h = img.size target_w, target_h = target_size # 取宽高缩放比的最小值,保证缩放后图像完全落在目标尺寸范围内 scale_ratio = min(target_w / origin_w, target_h / origin_h) new_w = int(round(origin_w * scale_ratio)) new_h = int(round(origin_h * scale_ratio)) # 等比缩放,新版本PIL用LANCZOS替代旧的ANTIALIAS,缩放画质更好 resized = img.resize((new_w, new_h), Image.Resampling.LANCZOS) # 新建目标尺寸的画布,填充指定底色 canvas = Image.new(img.mode if img.mode in ("RGB", "L") else "RGB", target_size, pad_color) # 把缩放后的图像贴到画布居中位置 offset_x = (target_w - new_w) // 2 offset_y = (target_h - new_h) // 2 canvas.paste(resized, (offset_x, offset_y)) return canvas
使用时直接替换你之前调用的Image.resize()、Image.thumbnail()、ImageOps.contain()即可,输出永远是严格的128×128尺寸。填充颜色可以根据数据集调整,比如用数据集像素均值、常用的中性灰(114,114,114),减少填充区域对模型的干扰。
方案2:PyTorch 生态实现
torchvision 官方已经内置了和上述逻辑完全一致的接口,不用重复造轮子,可以直接接入你现有的transforms流水线:
from torchvision import transforms from torchvision.transforms import functional as F # 自定义可复用的变换类 class ResizeKeepAspect(transforms.Resize): def forward(self, img): return F.resize_with_pad( img, size=self.size, interpolation=F.InterpolationMode.BILINEAR, fill=0, # 自定义填充颜色 antialias=self.antialias ) # 接入数据预处理流水线 data_transform = transforms.Compose([ ResizeKeepAspect((128, 128), antialias=True), transforms.ToTensor(), # 后续可以接归一化、增强等其他操作 ])
之前方法失效的原因说明
Image.thumbnail()是原地操作,只会对大于目标尺寸的图像做缩小,不会放大小于目标尺寸的图像,输出尺寸永远小于等于目标尺寸。ImageOps.contain()逻辑和thumbnail类似,仅保证图像不超出目标尺寸边界,不会做填充补边,自然没法输出严格固定的尺寸。- 直接传入元组参数调用
transforms.Resize((128,128))会强制将图像宽高直接拉伸到目标值,完全忽略原始宽高比,必然会导致图像变形。
如果是做目标检测、关键点检测这类需要映射原图坐标的任务,只需要在上述函数中把缩放比例、填充偏移量作为额外返回值输出,就可以直接用来还原坐标,逻辑完全通用。
内容的提问来源于stack exchange,提问作者Ammar Ul Hassan
相关产品推荐
相关产品推荐

