You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

计算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. 数据缩放不一致:方法1得到的是原始像素值(范围03500+)的均值,而方法2的结果`0.0584`是像素值被缩放到01范围后的均值。这是因为ToTensor()处理不同位深的tif图像时,会自动将像素值归一化到01区间(比如16位tif的原始范围是065535,转换后会除以65535),但方法1中可能存在部分图像未被正确缩放,或者你在方法1的环境中存在其他处理逻辑导致保留了原始值。
  2. 方法2逻辑错误:mean += rb.mean().sum()是冗余且错误的逻辑——rb.mean()已经是单图均值,.sum()不会改变结果,但正确的累计逻辑应该是累加单图的像素总和,而非均值。将代码改为mean += rb.sum(),再除以总像素数num_pixels,得到的结果会和方法1一致(因为单图均值求平均,本质与总像素求和除以总像素数等价)。
  3. 验证方式:在两种方法中添加print(rb.max(), rb.min()),就能直接看到是否存在像素值范围的差异,确认缩放问题。

内容的提问来源于stack exchange,提问作者imantha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:05:57