计算机视觉——Opencv blobFromImage与torchvision实现数据标准化

发布于:2024-05-10 ⋅ 阅读:(21) ⋅ 点赞:(0)

1.blobFromImage

blobFromImage 是 OpenCV 的深度神经网络(DNN)模块中的一个函数,它用于将图像转换为深度学习模型所需的输入格式,主要是对传入的图像进行的转换包括图像尺寸调整、均值减法、缩放等预处理步骤,以便图像数据能够适配深度学习模型的输入要求。

以下是 blobFromImage 函数的一些关键点:

  • 图像尺寸调整:函数可以根据需要调整图像的尺寸,以匹配神经网络的输入尺寸。
  • 均值减法:可以指定一个均值(mean),该函数会从图像的每个通道中减去这个均值。这通常用于数据中心化,以提高模型的训练和推理性能。
  • 缩放:通过 scalefactor 参数,可以对图像数据进行缩放,通常用于数据归一化。
  • 通道交换:如果 swapRB 参数设置为 true,则会交换图像的红色(R)和蓝色(B)通道,因为 OpenCV 默认使用 BGR 格式,而某些神经网络框架使用 RGB 格式。
  • 裁剪:如果 crop 参数设置为 true,则在调整图像大小时进行中心裁剪,以确保输出尺寸与指定的尺寸精确匹配。
  • 数据类型:通过 ddepth 参数可以指定输出 blob 的深度,通常选择 CV_32F(32位浮点数)。
  • 批量处理:还有一个对应的函数 blobFromImages,用于将一系列图像转换为一个批量的 blob,这在处理图像批次时更为高效。
    这个函数是 OpenCV DNN 模块中进行图像预处理的关键步骤,它使得 OpenCV 能够与多种深度学习框架无缝集成,进行图像分类、目标检测、语义分割等任务。
    例如,在进行图像分类时,可以使用 blobFromImage 对输入图像进行预处理,然后通过神经网络模型进行推理,得到预测结果。在 OpenCV 的示例和相关博客文章中,提供了如何使用这个函数的详细说明和示例代码。

函数原型

Mat cv::dnn::blobFromImage	(	
    InputArray 	image,
    double 	scalefactor = 1.0,
    const Size & 	size = Size(),
    const Scalar & 	mean = Scalar(),
    bool 	swapRB = false,
    bool 	crop = false,
    int 	ddepth = CV_32F 
)

OpenCV中的DNN模块包含blobFromImage方法对输入神经网络的图像进行处理,blobFromImage函数执行的操作及顺序为:

  • 先相对于原图像中心resize,crop
  • 再减均值
  • 像素值缩放0-255 -> 0-1
  • 图像数据通道转换,RGB->BGR
  • 返回一个NCHW 数组

2.torchvision

torchvision库中,transforms模块提供了一系列用于数据增强和预处理的函数,这些函数可以在数据加载后、模型训练前对图像数据进行操作,以适配深度学习模型的输入要求。ToTensorNormalize是两个常用的转换方法,它们通常联合使用来实现图像数据的标准化。

ToTensor转换的作用是将图像数据转换为PyTorch模型可以接受的格式。具体来说,它执行以下操作:

  1. 转换数据类型:将输入图像的数据类型从uint8转换为浮点数(通常是float32)。
  2. 维度变换:将图像的维度从HWC(高度、宽度、通道数)变换为CHW(通道数、高度、宽度)。这是因为PyTorch模型通常期望输入数据遵循这种维度顺序。
  3. 缩放像素值:将像素值从范围0-255线性缩放到0-1。这是通过将每个像素值除以255来实现的。

Normalize

Normalize转换用于对图像数据进行标准化处理,通常在ToTensor之后使用。它主要执行以下操作:

  1. 减去均值:从图像的每个通道中减去指定的均值。均值通常是根据整个数据集的每个通道的像素值计算得到的。
  2. 除以标准差:将减去均值后的图像数据除以对应通道的标准差。这有助于进一步规范化数据,使其具有单位方差。

标准化公式是: x − mean std \frac{x - \text{mean}}{\text{std}} stdxmean,其中x是原始像素值,mean是像素值的均值,std是标准差。

这两个转换方法的联合使用可以有效地对输入图像进行预处理,使其格式和数值范围适应深度学习模型的需要。这样做不仅有助于模型的训练过程,还可以提高模型的泛化能力。在实际应用中,这些转换通常被组合在一起作为一个预处理管道,可以方便地应用于数据集中的每个图像。

3.示例

通过一段简单的程序介绍cv2.dnn.blobFromImage执行的操作与torchvision中的ToTensor+Normalize效果等同

import cv2
import torch
import torchvision
from torchvision import transforms
import numpy as np
from PIL import Image

# 生成一个随机颜色的4x4图像,形状为 (高度, 宽度, 通道数)
dd = np.random.randint(0, 255, (5, 5, 3), dtype=np.uint8)
print(f"原始图像 dd: {dd}")

print("===>>> 使用 PyTorch 的 torchvision 进行预处理")

# 将 NumPy 数组转换为 PyTorch 张量
tt = torch.tensor(dd)
print(f"转换后的张量 tt: {tt}")

# 将 NumPy 数组转换为 PIL 图像
tp = Image.fromarray(dd)

# 使用 torchvision 的 Compose 来串联多个转换操作
trans = transforms.Compose([
    transforms.ToTensor(),  # 将 PIL 图像转换为张量,执行 HWC 到 CHW 的维度变换,并将像素值从 0-255 转换为 0-1
    transforms.Normalize((.5, .5, .5), (1., 1., 1.))  # 标准化张量,减去均值 (.5, .5, .5) 并除以标准差 (1., 1., 1.)
])

# 应用转换操作
trans_tt = trans(tp)
print(trans_tt)
print(f"转换后的张量形状 trans_tt.shape: {trans_tt.shape}")

print("===>>> 使用 OpenCV 的 cv2.dnn.blobFromImage 进行预处理")

# 使用 OpenCV 的 blobFromImage 函数进行预处理
# 参数说明:
# dd: 输入图像
# 1/255: 缩放比例,将像素值从 0-255 转换为 0-1
# (4, 4): 目标图像尺寸,由于输入图像已经是 4x4,所以这里不执行尺寸变换
# (127.5, 127.5, 127.5): 用于减去的均值
# False, False: 分别表示不交换 R 和 B 通道,不进行裁剪
blob = cv2.dnn.blobFromImage(dd, 1/255, (5, 5), (127.5, 127.5, 127.5), False, False)
print(f"OpenCV blobFromImage 输出的 blob: {blob}")
print(f"blob 的形状 blog.shape: {blob.shape}")

上述代码的输出为:

原始图像 dd: [[[ 24  53  15]
  [  4 172 202]
  [133  72  98]
  [128  19 201]
  [174 141  57]]

 [[ 71 176 174]
  [242 166 134]
  [139 157 153]
  [160 104 222]
  [208  71 191]]

 [[ 21 104 241]
  [173 199 116]
  [222  97  19]
  [ 76 222 237]
  [220  41  78]]

 [[175 254  71]
  [106  44  23]
  [  1 142 205]
  [157 236 211]
  [214 235 128]]

 [[246 104 169]
  [186 112 187]
  [176 181 251]
  [108 232 173]
  [203  25  55]]]
===>>> 使用 PyTorch 的 torchvision 进行预处理
转换后的张量 tt: tensor([[[ 24,  53,  15],
         [  4, 172, 202],
         [133,  72,  98],
         [128,  19, 201],
         [174, 141,  57]],

        [[ 71, 176, 174],
         [242, 166, 134],
         [139, 157, 153],
         [160, 104, 222],
         [208,  71, 191]],

        [[ 21, 104, 241],
         [173, 199, 116],
         [222,  97,  19],
         [ 76, 222, 237],
         [220,  41,  78]],

        [[175, 254,  71],
         [106,  44,  23],
         [  1, 142, 205],
         [157, 236, 211],
         [214, 235, 128]],

        [[246, 104, 169],
         [186, 112, 187],
         [176, 181, 251],
         [108, 232, 173],
         [203,  25,  55]]], dtype=torch.uint8)
tensor([[[-0.4059, -0.4843,  0.0216,  0.0020,  0.1824],
         [-0.2216,  0.4490,  0.0451,  0.1275,  0.3157],
         [-0.4176,  0.1784,  0.3706, -0.2020,  0.3627],
         [ 0.1863, -0.0843, -0.4961,  0.1157,  0.3392],
         [ 0.4647,  0.2294,  0.1902, -0.0765,  0.2961]],

        [[-0.2922,  0.1745, -0.2176, -0.4255,  0.0529],
         [ 0.1902,  0.1510,  0.1157, -0.0922, -0.2216],
         [-0.0922,  0.2804, -0.1196,  0.3706, -0.3392],
         [ 0.4961, -0.3275,  0.0569,  0.4255,  0.4216],
         [-0.0922, -0.0608,  0.2098,  0.4098, -0.4020]],

        [[-0.4412,  0.2922, -0.1157,  0.2882, -0.2765],
         [ 0.1824,  0.0255,  0.1000,  0.3706,  0.2490],
         [ 0.4451, -0.0451, -0.4255,  0.4294, -0.1941],
         [-0.2216, -0.4098,  0.3039,  0.3275,  0.0020],
         [ 0.1627,  0.2333,  0.4843,  0.1784, -0.2843]]])
转换后的张量形状 trans_tt.shape: torch.Size([3, 5, 5])
===>>> 使用 OpenCV 的 cv2.dnn.blobFromImage 进行预处理
OpenCV blobFromImage 输出的 blob: [[[[-0.40588236 -0.48431373  0.02156863  0.00196078  0.18235295]
   [-0.22156863  0.4490196   0.04509804  0.12745099  0.3156863 ]
   [-0.41764706  0.17843138  0.37058824 -0.20196079  0.3627451 ]
   [ 0.18627451 -0.08431373 -0.49607843  0.11568628  0.3392157 ]
   [ 0.46470588  0.22941177  0.19019608 -0.07647059  0.29607844]]

  [[-0.29215688  0.17450981 -0.21764706 -0.4254902   0.05294118]
   [ 0.19019608  0.1509804   0.11568628 -0.09215686 -0.22156863]
   [-0.09215686  0.28039217 -0.11960784  0.37058824 -0.3392157 ]
   [ 0.49607843 -0.327451    0.05686275  0.4254902   0.42156863]
   [-0.09215686 -0.06078431  0.20980392  0.40980393 -0.4019608 ]]

  [[-0.44117647  0.29215688 -0.11568628  0.2882353  -0.2764706 ]
   [ 0.18235295  0.0254902   0.1         0.37058824  0.24901961]
   [ 0.44509804 -0.04509804 -0.4254902   0.42941177 -0.19411765]
   [-0.22156863 -0.40980393  0.30392158  0.327451    0.00196078]
   [ 0.1627451   0.23333333  0.48431373  0.17843138 -0.28431374]]]]
blob 的形状 blog.shape: (1, 3, 5, 5)