在实际训练中,最常见也最简单的做法,就是在送入网络前把所有图片「变形」到同一个分辨率(比如 256×256 或 224×224),或者先裁剪/填充成同样大小。具体而言,可以分成以下几类方案:

一、图像分类


1. 直接缩放(Resize)

from torchvision import transformstransform = transforms.Compose([transforms.Resize((256, 256)),   # 不保留长宽比,强行拉伸transforms.ToTensor(),transforms.Normalize(mean, std),
])

优点:实现最简单、速度最快。
缺点:会改变图片的长宽比,可能带来形变,对模型性能略有影响。


2. 缩放+中心/随机裁剪(Resize + Crop)

transform = transforms.Compose([transforms.Resize(256),               # 保留长宽比,将短边缩放到256transforms.CenterCrop(224),           # 或 RandomCrop(224)transforms.ToTensor(),transforms.Normalize(mean, std),
])

流程:先把短边缩放到固定值(如 256 ),长边按比例变化,然后再在中心(或随机位置)裁剪出 224×224 的小块。
优点:保留了部分原始长宽比信息;RandomCrop还带来数据增强效果。
缺点:如果目标物体刚好出现在被裁掉的区域,会导致信息丢失。


3. 随机缩放+裁剪(RandomResizedCrop)

transform = transforms.Compose([transforms.RandomResizedCrop(size=224,scale=(0.8, 1.0),      # 随机裁出面积占原图的80%~100%ratio=(3/4, 4/3)       # 随机裁出长宽比范围),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize(mean, std),
])

原理:先随机在原图上裁一个区域(面积和长宽比都随机),再缩放到固定大小。
优点:兼顾数据增强和固定输入大小,常用于 ImageNet 级别的训练。
缺点:对小物体定位不友好,如果裁得太小可能把主体裁走。


4. 填充(Padding)后缩放

from PIL import ImageOpsdef pad_to_square(img, fill=0):w, h = img.sizepad = (max(h-w,0)//2, max(w-h,0)//2,max(h-w,0)-max(h-w,0)//2, max(w-h,0)-max(h-w,0)//2)return ImageOps.expand(img, pad, fill)transform = transforms.Compose([transforms.Lambda(pad_to_square),     # 填充为正方形transforms.Resize((256,256)),transforms.ToTensor(),transforms.Normalize(mean,std),
])

用途:先把图片填充成正方形(padding),再统一缩放;可保留原始物体比例,不会变形。
缺点:padding 部分引入了无效像素,浪费计算。


5. 自适应池化——支持任意输入尺寸

如果网络里最后用的是全局平均池化(nn.AdaptiveAvgPool2d((1,1)))而不是全连接层,那么理论上就可以接受任意分辨率的输入:

class SimpleCNN(nn.Module):def __init__(self, num_classes=2):super().__init__()self.features = nn.Sequential(# … 多层卷积+BN+ReLU …)# 全局自适应池化,输出固定 (batch, C, 1, 1)self.global_pool = nn.AdaptiveAvgPool2d((1,1))self.classifier = nn.Linear(C, num_classes)def forward(self, x):x = self.features(x)x = self.global_pool(x)x = x.view(x.size(0), -1)return self.classifier(x)

优点:网络本身对输入尺寸不敏感,可以直接给不同分辨率图;实际上很多 modern 结构(如 ResNet)就是这样做的。
缺点:当输入分辨率变化很大时,提取到的特征尺度也会差异较大,训练和推理时的 batch 大小、显存使用也会不一致。


小结

  1. 最常用 的是先统一缩放(Resize)或缩放+裁剪(Resize+Center/RandomCrop),这样既简单又有效。

  2. 进阶增强 可以用 RandomResizedCrop,同时带来固定输入大小和更多的数据增强。

  3. 如果要 严格保留长宽比,可以先 pad 到正方形,再 Resize

  4. 若希望网络天然支持 任意尺寸,可使用全卷积+自适应池化(AdaptiveAvgPool2d)结构。

根据需求(偏训练速度、保留比例还是增强多样性),可以选择或组合以上方案。

二、图像分割

在语义分割任务中,模型不仅要预测每个像素的类别,还要保证输入图像和标签(pixel-wise mask)在空间上严格对应。不同分辨率/长宽比的处理,跟分类任务类似,但要额外注意对 mask 做相同的变换。常见做法包括:


1. 固定大小的缩放+裁剪

和分类里一样,最简单也最常用的方式是先把图像和对应的 mask 同时缩放(Resize)或缩放+裁剪(CenterCrop/RandomCrop)到固定尺寸。

import torchvision.transforms.functional as Fdef train_transform(image, mask):# 假设我们要最终得到 512×512 的输入# 1) 将短边 resize 到 512,保留长宽比image = F.resize(image, size=512, interpolation=Image.BILINEAR)mask  = F.resize(mask,  size=512, interpolation=Image.NEAREST)# 2) 从中心或随机裁剪出 512×512i, j, h, w = transforms.RandomCrop.get_params(image, (512, 512))image = F.crop(image, i, j, h, w)mask  = F.crop(mask,  i, j, h, w)# 3) 转 tensor + 标准化image = F.to_tensor(image)image = F.normalize(image, mean, std)mask  = torch.as_tensor(np.array(mask), dtype=torch.long)return image, mask

优点:实现简单、能批量训练。
缺点:裁剪可能丢掉部分对象;大目标/小目标比例失真。


2. 保持长宽比的填充(Pad)

如果想严格保留原图的长宽比,可以先对图像和掩码的长和宽中较大值缩放到目标size,然后再将长或宽中较小的一侧padding到一个目标的长宽size,下面给出一种「先缩放长/宽中较大的一边到目标尺寸,再对另一边做等比填充」的实现。这样在缩放阶段处理的像素更少,最后再补齐到固定大小,计算量更低。

from PIL import Image, ImageOps
import mathdef resize_then_pad(image: Image.Image,mask: Image.Image,target_size: int = 512,pad_value_img: int = 0,pad_value_mask: int = 255):"""1) 将 image 和 mask 的长宽中较大的一边缩放到 target_size,保持长宽比2) 对另一边做对称 pad,使得最终尺寸为 target_size×target_sizemask 推荐用一个不会和类别冲突的填充值(如 255)表示 padding 区域,然后在 loss 里忽略这些像素。"""w, h = image.size# 计算缩放比例scale = target_size / max(w, h)new_w, new_h = math.floor(w * scale), math.floor(h * scale)# 首先缩放image = image.resize((new_w, new_h), Image.BILINEAR)mask  = mask.resize((new_w, new_h),  Image.NEAREST)# 计算需要 pad 的像素数pad_w = target_size - new_wpad_h = target_size - new_hpad_left   = pad_w // 2pad_right  = pad_w - pad_leftpad_top    = pad_h // 2pad_bottom = pad_h - pad_top# 对称 paddingimage = ImageOps.expand(image,border=(pad_left, pad_top, pad_right, pad_bottom),fill=pad_value_img)mask = ImageOps.expand(mask,border=(pad_left, pad_top, pad_right, pad_bottom),fill=pad_value_mask)return image, mask

用法示例

from torchvision import transforms
import torchvision.transforms.functional as Fdef train_transform(img, msk):# 1. 先 resize + padimg, msk = resize_then_pad(img, msk, target_size=512)# 2. 随机水平翻转等增强if random.random() > 0.5:img = F.hflip(img)msk = F.hflip(msk)# 3. 转 Tensor,并标准化img = F.to_tensor(img)img = F.normalize(img, mean, std)msk = torch.as_tensor(np.array(msk), dtype=torch.long)return img, msk
  • 优点

    • 缩放操作只针对最大边,像素量更少;

    • 填充操作只在小边上,不会引入太大额外计算;

    • 保持了原始长宽比,填充区域可在损失中忽略(填充值255)。

  • 注意:pad_value_mask 选一个不会冲突的类别 id,并在 loss 里用 ignore_index=pad_value_mask

原因:很多网络(如 U-Net、DeepLab 等)在下采样/上采样时要求输入尺寸能被 2^N 整除,否则拼接或上采样对齐会出错。
优点:无失真;只在边缘 padding。
缺点:padding 区域对模型无意义,可能需要在损失中忽略这些像素。

只需要在定义损失函数的时候,告诉它把填充值(pad_value_mask)对应的像素跳过即可。以下是一个最常见的做法,假设你把 padding 区域在 mask 中标记为 255,那么可以这样写:

import torch
import torch.nn as nn
import torch.nn.functional as F# 假设 pad_value_mask = 255
pad_value_mask = 255
num_classes = 21  # 举例:语义分割一共 0~20 类# 定义损失函数,ignore_index 会自动忽略 mask 中等于 255 的位置
criterion = nn.CrossEntropyLoss(ignore_index=pad_value_mask)# 假设 model 输出 logits 大小是 [B, C, H, W],mask 是 LongTensor [B, H, W]
logits = model(images)            # [B, C, H, W]
loss = criterion(logits, masks)   # masks 中值为 255 的像素不参与计算
loss.backward()

如果你更喜欢函数式的写法,也可以用 F.cross_entropy

loss = F.cross_entropy(logits, masks, ignore_index=pad_value_mask
)

原理说明

  • CrossEntropyLoss(以及底层的 F.cross_entropy)接收一个 ignore_index 参数,所有目标标签等于这个值的像素都会被跳过,不计入损失,也不会参与梯度更新。

  • 通常我们在 padding mask 时,选一个超出真实类别范围的整数(比如 255),以保证不会和真实标签冲突。

  • DataLoader 里,把 mask 转成 torch.long,padding 时用同样的填充值:

    # pad_value_mask = 255
    mask = ImageOps.expand(mask, border, fill=pad_value_mask)
    mask = torch.as_tensor(np.array(mask), dtype=torch.long)
    

这样就能确保模型训练时「看不见」那些 padding 区域,也不会因为它们引入噪声。


3. 随机裁剪成小块(Patch-based Training)

如果原图非常大(如 2000×1500),直接缩放到 512×512 会失去细节;更好的做法是“打补丁”——随机从原图上裁多个小块(patch),训练时每个 patch 都是固定大小:

# 假设想要 512×512 的训练块
i, j, h, w = transforms.RandomCrop.get_params(image, (512,512))
patch_img  = F.crop(image, i, j, h, w)
patch_mask = F.crop(mask,  i, j, h, w)
# 然后继续 ToTensor、Normalize…

优点:保留高分辨率细节;增强了数据多样性。
缺点:训练时 batch 内 patch 来自不同原图位置,可能导致上下文不完整,需要更多样本。


4. 任意尺寸输入+全卷积结构

许多经典分割网络(FCN、U-Net、DeepLab)都是全卷积(no fully-connected layers)的,最末端用 nn.AdaptiveAvgPool2d 或直接上采样到原始分辨率。这样在推理阶段就可以直接输入任意尺寸的图,网络会输出同样 spatial 大小的分割图:

class FCNSegmenter(nn.Module):def __init__(self, n_classes):super().__init__()self.backbone = torchvision.models.resnet34(pretrained=True)self.fc     = nn.Conv2d(512, n_classes, kernel_size=1)def forward(self, x):# x: B×3×H×Wfeat = self.backbone.conv1(x)# …一路下采样…out  = self.fc(feat)               # B×C×h×wseg  = F.interpolate(out, size=x.shape[2:], mode='bilinear', align_corners=False)return seg                        # B×C×H×W

优点:不需要预先 resize/crop;推理时更灵活。
缺点:训练时若 batch 中样本尺寸差异过大,显存占用和速度难以控制;通常仍在训练时对图像做上述固定大小处理。


5. Sliding-Window/Tiling 推理

对于超大输入(比如遥感影像或医学切片),即使网络结构支持任意大小,也会因为内存限制或上下文窗口有限而采用“滑窗”(sliding window)推理:

  1. 将大图按一定步长分块(patch),

  2. 对每块做分割预测,

  3. 将 patch 输出拼接回全图,并对重叠区域取平均或最大概率。

常见工具:

  • MMsegmentation 中的 inference_sliding

  • TorchVision 中的 SlidingWindowInferer


小结建议

  • 训练:绝大多数场景还是先把图和 mask 同步变换到固定大小(如 512×512),或 patch-based 随机裁剪。

  • 推理:若输入大小可变且不大,用全卷积网络直接预测;若图像过大,则用滑窗策略。

  • 注意点:所有几何变换(resize、crop、flip、pad)都要对 image 和 mask 一致地做,而且 mask 要用最近邻插值(interpolation=Image.NEAREST)以保证类别标签不被混叠。

这样,既能保证批量训练的稳定性和效率,也能在推理时灵活应对各种分辨率和长宽比。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92230.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92230.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/92230.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pytest-log

问题1:我们在运行测试用例的时候如何记录测试的log,如何使用?问题2:我写的函数,为了方便log记录,但是在pytest运行时,会兼容pytest且不会重复记录,怎么解决?1、pytest有内…

在安卓源码中添加自定义jar包给源码中某些模块使用

一、具体步骤 1. 准备目录与 Jar 包 在vendor下 创建新的模块目录,放入demo.jar 包: demojar/ # 模块目录 ├── Android.bp # 编译配置文件 └── demo.jar 2. 编写 Android.bp 配置 Android.bp 示例配置: java_import {…

buntu 22.04 上离线安装Docker 25.0.5(二)

以下有免费的4090云主机提供ubuntu22.04系统的其他入门实践操作 地址:星宇科技 | GPU服务器 高性能云主机 云服务器-登录 相关兑换码星宇社区---4090算力卡免费体验、共享开发社区-CSDN博客 兑换码要是过期了,可以私信我获取最新兑换码!&a…

初探 Web 环境下的 LLM 安全:攻击原理与风险边界

文章目录前言1 什么是大型语言模型(LLM)?1.1 LLM的核心特征1.2 LLM在Web场景中的典型应用2 LLM攻击的核心手段:提示注入与权限滥用3 LLM与API集成的安全隐患:工作流中的漏洞节点3.1 LLM-API集成的典型工作流3.2 工作流…

【新手向】PyTorch常用Tensor shape变换方法

【新手向】PyTorch常用Tensor shape变换方法 前言 B站UP主科研水神大队长的视频中介绍了“缝合模块”大法,其中专门强调了“深度学习 玩的就是shape”。受此启发,专门整理能够调整tensor形状的几个内置函数,方便以后更好地调整PyTorch代码中的…

React 18 vs Vue3:状态管理方案深度对比

🔥 背景: React有Redux、Zustand、Jotai等方案 Vue有Pinia、Vuex 4.x 如何选择适合项目的方案? 🔍 核心对比: 维度 React (Redux Toolkit) Vue3 (Pinia) 类型安全 ✅ 需手动配置TS ✅ 自动类型推导 代码量 较多(需写action) 较少(类似Vuex 5) 响应式原理 不可变数据…

UE5网络联机函数

Find Sessions Create Session Join Session Destroy Session Steam是p2p直接联机 一、steam提供的测试用AppId AppId是steam为每一款游戏所设定的独有标识,每一款要上架steam的游戏都会拥有独一无二的AppId。不过为了方便开发者测试,steam提供了游…

Spring Boot 监控:AOP vs Filter vs Java Agent

01前言 在 高并发 微服务 中, 传统 手动埋点(System.currentTimeMillis())就像用体温计量火箭速度——代码侵入、重复劳动、维护爆炸。 下文是无侵入、高精度、全链路 监控 API 耗时,全程不碰业务代码的方案! 02实战&…

基于Android的电子记账本系统

博主介绍:java高级开发,从事互联网行业多年,熟悉各种主流语言,精通java、python、php、爬虫、web开发,已经做了多年的毕业设计程序开发,开发过上千套毕业设计程序,没有什么华丽的语言&#xff0…

7月17日日记

结束了数学建模之后的这两天一直在紧张的复习,但是说实话效率有点低,因为可能觉得自己找到了两个小时速成课,觉得无所谓了,所以有点放松了。在宿舍杰哥和林雨城却一直在复习,感觉他们的微积分和线性代数复习的都比我好…

Linux下SPI设备驱动开发

一.SPI协议介绍1.硬件连接介绍引脚含义:DO(MOSI):Master Output, Slave Input,SPI主控用来发出数据,SPI从设备用来接收数据。DI(MISO):Master Input, Slave Output,SPI主控用来发出数据,SPI从设…

用Dify构建气象智能体:从0到1搭建AI工作流实战指南

作为一名Agent产品经理,我最近在负责气象智能体的建设项目。传统气象服务面临三大痛点:数据孤岛严重(气象局API、卫星云图、地面观测站等多源数据格式不一)、响应链路长(从数据采集到预警发布需人工介入多个环节)、交互体验单一(用户只能被动接收标准化预警,无法个性化…

Android NDK ffmpeg 音视频开发实战

文章目录接入FFmpeg1.下载FFmpeg 源码2.编译FFmpeg.so库异常处理3.自定义FFmpeg交互so库创建4.配置CMakeLists.txt5.CMakeLists.txt 环境配置6.Native与Java层调用解码器准备接入FFmpeg 1.下载FFmpeg 源码 FFmpeg官网地址 2.编译FFmpeg.so库 移动 FFmpeg 源码文件夹至 Andr…

使用 go-redis-entraid 实现 Entra ID 无密钥认证

1、依赖与安装 步骤命令说明安装(或升级) go-redis v9.9go get github.com/redis/go-redis/v9latestentraid 必须 ≥ 9.9.0安装 go-redis-entraidgo get github.com/redis/go-redis-entraid自动拉取 transit 依赖 2、认证方式一览 方式说明创建 Stream…

window上docker安装RabbitMQ

1、要进http://localhost:15672管理页面需要安装management版本2、搜索镜像并pull3、启动镜像时将端口映射出来4、启动成功,点击可查看日志详情,浏览器访问5、直接使用guest/guest登录会报错User can only log in via localhost解决办法有两个&#xff1…

异世界历险之数据结构世界(排序(插入,希尔,堆排))

前言 介绍 插入排序 基本知识: 直接插入排序是一种简单的插入排序法,其基本思想是: 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 直接插入…

oracle 数据库中,将几张表的数据按指定日期范围实时同步至同一个数据库的备份表中。

以下是一个Oracle数据库中实现表数据按指定日期范围实时同步至备份表的解决方案。这个方案使用存储过程和触发器组合实现: 1. 创建备份表结构 首先需要为每张需要备份的表创建对应的备份表,结构与原表相同: -- 为原表创建备份表(示…

电脑网络连接正常,微信、QQ能正常使用,但无法访问网页?DNS问题的解决方案和背后原理。

文章目录1. 问题背景2. 解决方案2.1 手动刷新DNS2.1.1 Windows版本2.1.2 Mac版本2.2 手动设置DNS服务器2.2.1 Windows版2.2.2 Mac版2.3 其他解决方案3. DNS是什么?3.1 详细解释DNS3.1.1 A distributed, hierarchical database(一个分布式和分层数据库结构…

【HTML】图片比例和外部div比例不一致,最大程度占满

图片比例和外部div比例不一致&#xff0c;最大程度占满&#xff0c;并且图片比例不变 其中1.jpg,2.jpg,1.html在同一目录 |-----|- 1.jpg|- 2.jpg|- 1.html1.jpg2.jpg<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /&g…

如何使用python网络爬虫批量获取公共资源数据技术

如何快速批量地获取海量公共资源数据决定了科研的效率。Python网络爬虫是快速批量获取网络数据的重要手段&#xff0c;它按照发送请求、获得页面、解析页面、下载内容、储存内容等流程&#xff1f; 一&#xff1a;Python软件的安装及入门1 Python软件安装及入门1)Anaconda软件安…