如何对一个4D numpy数组进行归一化处理?

18

我有一个三维的numpy图像数组(CIFAR-10数据集)。图像数组的形状如下:

a = np.random.rand(32, 32, 3)

在进行深度学习之前,我想对数据进行标准化以获得更好的结果。对于一个一维数组,我知道我们可以像这样进行最小最大归一化:

v = np.random.rand(6)
(v - v.min())/(v.max() - v.min())

Out[68]:
array([ 0.89502294,  0.        ,  1.        ,  0.65069468,  0.63657915,
        0.08932196])

然而,当涉及到3D数组时,我完全迷失了。具体而言,我有以下问题:

  1. 我们沿哪个轴取最小值和最大值?
  2. 如何在3D数组中实现此操作?

感谢您的帮助!


编辑: 事实证明,我需要处理一个形状为(202, 32, 32, 3)的4D Numpy数组,因此第一维将是图像的索引,而最后3个维度是实际图像。如果有人能够提供用于归一化这样一个4D数组的代码,那将非常好。谢谢!


编辑2: 感谢@Eric下面的代码,我已经搞定了:

x_min = x.min(axis=(1, 2), keepdims=True)
x_max = x.max(axis=(1, 2), keepdims=True)

x = (x - x_min)/(x_max-x_min)

2
你们的堆栈是4D(图像编号 x X x Y x RGB)还是3D(图像编号 x X x Y)? - Paul Panzer
3个回答

22
假设您正在处理形状为(W, H, 3)的图像数据,您应该分别对每个通道(axis=2)进行归一化,如其他答案中所提到的。
您可以使用以下方法实现:
# keepdims makes the result shape (1, 1, 3) instead of (3,). This doesn't matter here, but
# would matter if you wanted to normalize over a different axis.
v_min = v.min(axis=(0, 1), keepdims=True)
v_max = v.max(axis=(0, 1), keepdims=True)
(v - v_min)/(v_max - v_min)

谢谢您的回复!实际上我需要处理一个4D数组(我已经更新了我的帖子)。您能否提供进一步的指导? - George Liu
我已经通过你的代码找到了如何做到这一点! - George Liu
1
@George:是的,你的想法是正确的 - 你正在对每个图像和通道(轴0和3)取x和y(轴1和2)的最大值。 - Eric

4
  1. 我们取最小值和最大值的轴是哪个?

为了回答这个问题,我们可能需要更多关于你的数据的信息。但是一般来说,例如讨论三通道图像时,我们会使用每个通道的最小值和最大值进行归一化。这意味着我们需要针对每个通道执行归一化操作,共计3次。

以下是一个示例:

    img = numpy.random.randint(0, 100, size=(10, 10, 3))  # Generating some random numbers
    img = img.astype(numpy.float32)  # converting array of ints to floats
    img_a = img[:, :, 0]
    img_b = img[:, :, 1]
    img_c = img[:, :, 2]  # Extracting single channels from 3 channel image
    # The above code could also be replaced with cv2.split(img) << which will return 3 numpy arrays (using opencv)

    # normalizing per channel data:
    img_a = (img_a - numpy.min(img_a)) / (numpy.max(img_a) - numpy.min(img_a))
    img_b = (img_b - numpy.min(img_b)) / (numpy.max(img_b) - numpy.min(img_b))
    img_c = (img_c - numpy.min(img_c)) / (numpy.max(img_c) - numpy.min(img_c))

    # putting the 3 channels back together:
    img_norm = numpy.empty((10, 10, 3), dtype=numpy.float32)
    img_norm[:, :, 0] = img_a
    img_norm[:, :, 1] = img_b
    img_norm[:, :, 2] = img_c

编辑:我刚想到,一旦你拥有一个通道的数据(例如32x32像素的图像),你可以直接使用以下代码:

from sklearn.preprocessing import normalize
img_a_norm = normalize(img_a)

我们如何使用3D数组?
这是一个比较大的问题。如果需要使用诸如数组最小值和最大值等函数,建议使用Numpy版本。例如,可以通过轴向分隔符来实现索引,正如我上面的示例所示。此外,请参考Numpy的ndarray文档 @https://docs.scipy.org/doc/numpy/reference/generated/numpy.ndarray.html 以获取更多信息。他们为n维数组提供了非常出色的工具。

1
这是错误的归一化方式,你必须从最大值中减去最小值。即 img_a = (img_a - numpy.min(img_a)) / (numpy.max(img_a) - numpy.min(img_a)) 确保括号正确。 - smttsp
@smttsp,你说得完全正确,我打错了,正在修正我的原始答案。谢谢。 - antonmik
1
我们需要进行3次归一化 - 每个通道一次。没错,但更好的表达方式是“在axis=-1上进行归一化”。不需要为每个通道重复一遍代码。 - Eric
r, g, b = cv2.split(img) can also be written r, g, b = np.moveaxis(img, -1, 0) - Eric
没错,许多基本的cv2功能最好用numpy操作表达。 - Eric
显示剩余2条评论

0

这里有不同的方法。您可以决定对整个图像批次进行归一化,也可以对单个图像进行归一化。为此,您可以使用单个图像的平均值或使用整个图像批次的平均值或使用来自另一个数据集的固定平均值 - 例如,您可以使用 ImageNet 的平均值。

如果您想要做与Tensorflow的tf.image.per_image_standardization相同的事情,则应该使用该图像的平均值对单个图像进行归一化。因此,您需要遍历所有图像,并对单个图像中的所有轴进行归一化,如下所示:

import math
import numpy as np
from PIL import Image

# open images
image_1 = Image.open("your_image_1.jpg")
image_2 = Image.open("your_image_2.jpg")
images = [image_1, image_2]
images = np.array(images)
standardized_images = []

# standardize images
for image in images:
    mean = image.mean()
    stddev = image.std()
    adjusted_stddev = max(stddev, 1.0/math.sqrt(image.size))
    standardized_image = (image - mean) / adjusted_stddev
    standardized_images.append(standardized_image)

standardized_images = np.array(standardized_images)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接