计算Torchvision Normalize所需均值标准差:两种方法结果差异原因
问题背景
我需要为3张图像计算通道均值和标准差,作为PyTorch中torchvision.transforms.Normalize函数的输入。我的数据集里3个颜色通道分别存储在单独的tif文件中,以下以红通道为例展示两种计算方法,但结果差异极大,想知道原因。
方法1:单图均值求平均
加载形状为1×120×120的张量,计算单张图像红通道的像素均值并存入列表,最后对列表求均值得到数据集红通道均值,标准差计算逻辑相同。
def get_mean_std(root:str): """ 计算数据集各通道的均值和标准差 输入参数 - root : 数据集根目录路径 """ rb_list = [] gb_list = [] bb_list = [] mean = 0 for data_folder in os.listdir(root)[:3]: # 包含12个tif文件和1个json文件的文件夹路径 data_folder_pth = os.path.join(root, data_folder) # RGB通道路径 | rb代表红波段... rb_pth = os.path.join(data_folder_pth, [f for f in os.listdir(data_folder_pth) if f.endswith("B04.tif")][0]) gb_pth = os.path.join(data_folder_pth, [f for f in os.listdir(data_folder_pth) if f.endswith("B03.tif")][0]) bb_pth = os.path.join(data_folder_pth, [f for f in os.listdir(data_folder_pth) if f.endswith("B02.tif")][0]) # 打开图像并转换为张量 rb = ToTensor()(Image.open(rb_pth)).float() #(1,120,120) gb = ToTensor()(Image.open(gb_pth)).float() #(1,120,120) bb = ToTensor()(Image.open(bb_pth)).float() #(1,120,120) # 计算单图所有像素的均值 rb_list.append(rb.mean().item()) mean_of_3_images = np.array(rb_list).mean() print(f"rb_list : {rb_list}") print(f"红通道均值 : {mean_of_3_images}") # 输出 >>> rb_list : [281.01361083984375, 266.2029113769531, 1977.7083740234375] >>> 红通道均值 : 841.6416320800781
方法2:累计像素求和求均值
参考SaturnCloud的方法适配数据集,统计所有像素总数和累计像素值总和,最后用总和除以像素总数得到均值。
def get_mean_std(root:str): """ 计算数据集各通道的均值和标准差 输入参数 - root : 数据集根目录路径 """ mean = 0 num_pixels = 0 for data_folder in os.listdir(root)[:3]: # 包含12个tif文件和1个json文件的文件夹路径 data_folder_pth = os.path.join(root, data_folder) # RGB通道路径 | rb代表红波段... rb_pth = os.path.join(data_folder_pth, [f for f in os.listdir(data_folder_pth) if f.endswith("B04.tif")][0]) gb_pth = os.path.join(data_folder_pth, [f for f in os.listdir(data_folder_pth) if f.endswith("B03.tif")][0]) bb_pth = os.path.join(data_folder_pth, [f for f in os.listdir(data_folder_pth) if f.endswith("B02.tif")][0]) # 打开图像并转换为张量 rb = ToTensor()(Image.open(rb_pth)).float() #(1,120,120) gb = ToTensor()(Image.open(gb_pth)).float() #(1,120,120) bb = ToTensor()(Image.open(bb_pth)).float() #(1,120,120) batch, height, width = rb.shape #(1,120,120) num_pixels += batch * height * width mean += rb.mean().sum() print(mean) print(mean / num_pixels) # 输出 >>> tensor(2524.9248) >>> tensor(0.0584)
核心疑问
两种方法得到的结果差异极大,原因是什么?
附3张图像红通道的张量示例:
tensor([[[322., 275., 262., ..., 260., 225., 268.], [283., 271., 259., ..., 277., 269., 278.], [302., 303., 276., ..., 305., 279., 283.], ..., [398., 341., 374., ..., 246., 273., 227.], [383., 351., 375., ..., 266., 277., 260.], [353., 347., 359., ..., 280., 260., 227.]]]) tensor([[[153., 214., 242., ..., 825., 575., 399.], [206., 223., 198., ..., 766., 507., 477.], [219., 256., 189., ..., 593., 365., 384.], ..., [138., 255., 329., ..., 227., 289., 334.], [174., 215., 276., ..., 402., 395., 350.], [216., 212., 214., ..., 354., 362., 312.]]]) tensor([[[1727., 1852., 1184., ..., 3494., 3539., 3374.], [1882., 1868., 1307., ..., 3523., 3443., 3278.], [1716., 1975., 1919., ..., 3280., 3319., 3121.], ..., [2199., 2214., 2269., ..., 2563., 2284., 2147.], [2181., 2213., 2312., ..., 2686., 2668., 2737.], [2208., 2297., 2351., ..., 2647., 2904., 3008.]]])
差异原因分析
- 数据缩放不一致:方法1得到的是原始像素值(范围03500+)的均值,而方法2的结果`0.0584`是像素值被缩放到01范围后的均值。这是因为
ToTensor()处理不同位深的tif图像时,会自动将像素值归一化到01区间(比如16位tif的原始范围是065535,转换后会除以65535),但方法1中可能存在部分图像未被正确缩放,或者你在方法1的环境中存在其他处理逻辑导致保留了原始值。 - 方法2逻辑错误:
mean += rb.mean().sum()是冗余且错误的逻辑——rb.mean()已经是单图均值,.sum()不会改变结果,但正确的累计逻辑应该是累加单图的像素总和,而非均值。将代码改为mean += rb.sum(),再除以总像素数num_pixels,得到的结果会和方法1一致(因为单图均值求平均,本质与总像素求和除以总像素数等价)。 - 验证方式:在两种方法中添加
print(rb.max(), rb.min()),就能直接看到是否存在像素值范围的差异,确认缩放问题。
内容的提问来源于stack exchange,提问作者imantha
相关产品推荐
相关产品推荐

