【问题标题】:Is this the correct way of whitening an image in python?这是在python中美白图像的正确方法吗?
【发布时间】:2017-01-13 13:28:33
【问题描述】:

我正在尝试 zero-centerwhiten CIFAR10 数据集,但我得到的结果看起来像随机噪声!
Cifar10 数据集包含 60,000 大小为 32x32 的彩色图像。训练集包含50,000,测试集分别包含10,000图像。
以下 sn-ps 代码显示了我为使数据集变白所做的过程:

# zero-center
mean = np.mean(data_train, axis = (0,2,3)) 
for i in range(data_train.shape[0]):
    for j in range(data_train.shape[1]):
        data_train[i,j,:,:] -= mean[j]

first_dim = data_train.shape[0] #50,000
second_dim = data_train.shape[1] * data_train.shape[2] * data_train.shape[3] # 3*32*32
shape = (first_dim, second_dim) # (50000, 3072) 

# compute the covariance matrix
cov = np.dot(data_train.reshape(shape).T, data_train.reshape(shape)) / data_train.shape[0] 
# compute the SVD factorization of the data covariance matrix
U,S,V = np.linalg.svd(cov)

print 'cov.shape = ',cov.shape
print U.shape, S.shape, V.shape

Xrot = np.dot(data_train.reshape(shape), U) # decorrelate the data
Xwhite = Xrot / np.sqrt(S + 1e-5)

print Xwhite.shape
data_whitened = Xwhite.reshape(-1,32,32,3)
print data_whitened.shape

输出:

cov.shape =  (3072L, 3072L)
(3072L, 3072L) (3072L,) (3072L, 3072L)
(50000L, 3072L)
(50000L, 32L, 32L, 3L)
(32L, 32L, 3L)

并尝试显示生成的图像:

import matplotlib.pyplot as plt
%matplotlib inline
from scipy.misc import imshow
print data_whitened[0].shape
fig = plt.figure()
plt.subplot(221)
plt.imshow(data_whitened[0])
plt.subplot(222)
plt.imshow(data_whitened[100])
plt.show()

顺便说一下data_train[0].shape(3,32,32), 但如果我根据我得到的重塑白化图像

TypeError: Invalid dimensions for image data

这可能只是一个可视化问题吗?如果是这样,我怎样才能确保是这样?

更新:
感谢@AndrasDeak,我以这种方式修复了可视化代码,但输出看起来仍然是随机的:

data_whitened = Xwhite.reshape(-1,3,32,32).transpose(0,2,3,1)
print data_whitened.shape
fig = plt.figure()
plt.subplot(221)
plt.imshow(data_whitened[0])

更新 2:
这是我在运行下面给出的一些命令时得到的: 如下图所示,toimage 可以很好地显示图像,但试图重塑它,会弄乱图像。

# output is of shape (N, 3, 32, 32)
X = X.reshape((-1,3,32,32))
# output is of shape (N, 32, 32, 3)
X = X.transpose(0,2,3,1)
# put data back into a design matrix (N, 3072)
X = X.reshape(-1, 3072)

plt.imshow(X[6].reshape(32,32,3))
plt.show()

出于某种奇怪的原因,这是我一开始得到的,但经过几次尝试,它变成了以前的图像。

【问题讨论】:

  • 我不熟悉美白,但是是的,您得到的错误是由于 plt.imshow 期望 (M,N,3) 形状的数组作为 RGB 图像。但是这个问题更深层次:我也不希望你的data_train 的形状是(N,3,32,32):它应该包含类似的行-列-RGB_通道尺寸模式。这表明您可能误解了输入的维度,这可以解释为什么您的输出不是您所期望的。
  • 哦,除非我弄错了,否则您所做的零中心化等同于矢量化data_train -= np.mean(data_train, axis = (0,2,3))[:,None,None],利用数组广播。
  • 最后评论:我希望零居中可以逐个图像工作。您将每个图像的每个颜色通道居中。这意味着(如果data_train 的最后两个维度对应于像素)您需要np.mean(data_train,axis=(2,3)),并且相应地需要data_train -= np.mean(data_train, axis = (0,2,3))[...,None,None]。不对吗?
  • 这可能是个愚蠢的问题,但你不能使用 ctypes 访问内存中的字节,并简单地用 (255,255,255) (假设为 RGB)覆盖它们吗?
  • 好的,我想我看到了(至少一个)问题。您只在代码中使用reshapes,但您从(3,32,32) 开始并以(32,32,3) 结束。这是错误的。如果您重塑数据而不是排列索引(使用.transpose),您将把数组元素全部混淆。那肯定是错的。我不确定这是否正确,但您可能正在寻找 data_whitened = Xwhite.reshape(-1,3,32,32).permute(0,2,3,1)

标签: python image-preprocessing image-whitening


【解决方案1】:

让我们来看看这个。正如您所指出的,CIFAR 包含存储在矩阵中的图像;每个图像是一行,每行有 3072 列 uint8 数字(0-255)。图像为 32x32 像素,像素为 RGB(三通道颜色)。

# https://www.cs.toronto.edu/~kriz/cifar.html
# wget https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
# tar xf cifar-10-python.tar.gz
import numpy as np
import cPickle
with open('cifar-10-batches-py/data_batch_1') as input_file: 
    X = cPickle.load(input_file)
X = X['data']   # shape is (N, 3072)

事实证明,列的顺序有点滑稽:所有红色像素值都排在第一位,然后是所有绿色像素,然后是所有蓝色像素。这使得查看图像变得很棘手。这个:

import matplotlib.pyplot as plt
plt.imshow(X[6].reshape(32,32,3))
plt.show()

给出这个:

所以,为了便于查看,让我们用reshapetranspose 调整矩阵的维度:

# output is of shape (N, 3, 32, 32)
X = X.reshape((-1,3,32,32))
# output is of shape (N, 32, 32, 3)
X = X.transpose(0,2,3,1)
# put data back into a design matrix (N, 3072)
X = X.reshape(-1, 3072)

现在:

plt.imshow(X[6].reshape(32,32,3))
plt.show()

给予:

好的,继续 ZCA 美白。我们经常被提醒,在白化数据之前将数据归零是非常重要的。此时,对您包含的代码进行观察。据我所知,计算机视觉将颜色通道视为另一个特征维度。图像中单独的 RGB 值没有什么特别之处,就像单独的像素值没有什么特别之处一样。它们都只是数字特征。因此,当您计算平均像素值时,考虑颜色通道(即,您的 meanr,g,b 值的元组),我们将只计算平均 image 值。请注意,X 是一个包含 N 行和 3072 列的大矩阵。我们会将每一列都视为与其他每一列“相同的事物”。

# zero-centre the data (this calculates the mean separately across
# pixels and colour channels)
X = X - X.mean(axis=0)

此时,我们还要进行全局对比度归一化,这通常应用于图像数据。我将使用 L2 范数,这使得每个图像的矢量幅度为 1:

X = X / np.sqrt((X ** 2).sum(axis=1))[:,None]

人们可以轻松地使用其他东西,例如标准偏差 (X = X / np.std(X, axis=0)) 或最小-最大缩放到某个区间,例如 [-1,1]。

快到了。在这一点上,我们还没有对我们的数据进行很大的修改,因为我们只是对其进行了移动和缩放(线性变换)。为了显示它,我们需要将图像数据返回到 [0,1] 范围内,所以让我们使用一个辅助函数:

def show(i):
    i = i.reshape((32,32,3))
    m,M = i.min(), i.max()
    plt.imshow((i - m) / (M - m))
    plt.show()

show(X[6])

这里的孔雀看起来稍微亮一些,但这只是因为我们拉伸了它的像素值以填充区间 [0,1]:

ZCA美白:

# compute the covariance of the image data
cov = np.cov(X, rowvar=True)   # cov is (N, N)
# singular value decomposition
U,S,V = np.linalg.svd(cov)     # U is (N, N), S is (N,)
# build the ZCA matrix
epsilon = 1e-5
zca_matrix = np.dot(U, np.dot(np.diag(1.0/np.sqrt(S + epsilon)), U.T))
# transform the image data       zca_matrix is (N,N)
zca = np.dot(zca_matrix, X)    # zca is (N, 3072)

看看(show(zca[6])):

现在孔雀肯定看起来不一样了。您可以看到 ZCA 已经在色彩空间中旋转了图像,因此它看起来就像是旧电视上的图片,色调设置不正常。不过还是可以认出来的。

大概是因为我使用了epsilon 值,我转换后的数据的协方差并不完全是同一性,但它相当接近:

>>> (np.cov(zca, rowvar=True).argmax(axis=1) == np.arange(zca.shape[0])).all()
True

1 月 29 日更新

我不完全确定如何解决您遇到的问题;您目前的问题似乎在于原始数据的形式,所以我建议您在尝试转向零中心和 ZCA 之前先解决这个问题。

一方面,您的更新中四个图的第一个图看起来不错,这表明您以正确的方式加载了 CIFAR 数据。第二个图是由toimage 制作的,我想,它会自动找出哪个维度有颜色数据,这是一个不错的技巧。另一方面,之后出现的东西看起来很奇怪,所以似乎某处出了点问题。我承认我不能完全遵循您的脚本状态,因为我怀疑您正在交互式工作(笔记本),当它们不起作用时重试(稍后会详细介绍),并且您正在使用代码你没有在你的问题中显示。特别是,我不确定您如何加载 CIFAR 数据;您的屏幕截图显示了一些print 语句(Reading training data... 等)的输出,然后当您将train_data 复制到X 并打印shapeX 时,形状已经被重新调整为@ 987654359@。就像我说的那样,更新情节 1 往往表明重塑已经正确发生。从图 3 和图 4 来看,我认为您在某处混淆了矩阵尺寸,所以我不确定您是如何进行整形和转置的。

请注意,由于以下原因,请务必小心整形和转置。 X = X.reshape(...)X = X.transpose(...) 代码正在修改矩阵就地。如果您多次执行此操作(就像在 jupyter notebook 中偶然发生的那样),您将一遍又一遍地打乱矩阵的轴,并且绘制数据将开始看起来非常奇怪。这张图片展示了我们迭代重塑和转置操作的过程:

这种进展不会循环回来,或者至少不会快速循环。由于数据中的周期性规律(如图像的 32 像素行结构),您往往会在这些不正确的整形转置图像中出现条带。我想知道这是否是您更新中四个情节中的第三个发生的情况,这看起来比您问题的原始版本中的图像要随机得多。

您更新的第四个情节是孔雀的彩色底片。我不确定你是怎么得到的,但我可以重现你的输出:

plt.imshow(255 - X[6].reshape(32,32,3))
plt.show()

给出:

如果您使用我的 show 辅助函数,并且您将 mM 混为一谈,您可以获得此结果的一种方法是:

def show(i):
    i = i.reshape((32,32,3))
    m,M = i.min(), i.max()
    plt.imshow((i - M) / (m - M))  # this will produce a negative img
    plt.show()

【讨论】:

  • 非常感谢,但我得到的结果完全不同。我用 3 张图片更新了问题,这些图片显示了它们的外观
  • 正确,您应该对测试集应用相同的转换。这意味着保存训练数据的mean 值和zca_matrix。对于对比度归一化,如果你这样做,并且如果你像我一样做 L2,你也可以对测试集进行 L2 归一化(即,没有参数可以从 L2 归一化的训练数据中学习) .如果您使用 stdev-normalization 或其他方法,您还应该对训练数据进行估计,然后将相同的估计应用于测试数据。
  • 谢谢,您确定 zca_matrix 吗?由于 zca_matrixs 的形状不会与 test_data 相同,因此 training_data 的 zca_matrix 比测试数据大得多。你能详细说明一下吗?
  • 嗯。也许使用cov = np.cov(X, rowvar=False)zca = np.dot(X, zca_matrix) 是一个更好的主意。然后zca_matrix 的形状为 (3072, 3072) 并且也可以在测试集上使用。 pylearn2 有一个 ZCA 美白的有效实现,我认为他们就是这样做的。
  • rowvar=True 计算训练样本之间的协方差; rowvar=False 计算维度之间的协方差。
【解决方案2】:

我遇到了同样的问题:生成的预测值是关闭的:

每个浮动图像的值都应该是 [0-1.0]

def toimage(data):
    min_ = np.min(data)
    max_ = np.max(data)
    return (data-min_)/(max_ - min_)

注意:此功能仅用于可视化!

但是请注意如何计算“去相关”或“白化”矩阵@wildwilhelm

zca_matrix = np.dot(U, np.dot(np.diag(1.0/np.sqrt(S + epsilon)), U.T))

这是因为相关矩阵的特征向量的 U 矩阵实际上是这个:SVD(X) = U,S,V 但 U 是 X*X 的特征基而不是 X https://en.wikipedia.org/wiki/Singular-value_decomposition

作为最后一点,我宁愿只考虑统计单位像素和 RGB 通道它们的模态,而不是图像作为统计单位和像素作为模态。 我已经在 CIFAR 10 数据库上进行了尝试,效果非常好。

图像示例:顶部图像具有“withened”的 RGB 值,底部是原始图像

图像示例 2:训练和损失中没有 ZCA 变换性能

图像示例 3:ZCA 变换在训练和损失中的表现

【讨论】:

  • 您能说得详细一点吗?这里有什么问题?我很困惑!
  • 您能否分享您的美白脚本,以防它与我的不同(paste.ee/p/WSHuv#s=0)?还是白化的 cifar10 数据集本身?我不断遇到内存问题。
【解决方案3】:

如果您想线性缩放图像以使其具有零均值和单位范数,您可以执行与 Tensofrlow 的 tf.image.per_image_standardization 相同的图像白化。在文档之后,您需要使用以下公式来规范化每个图像独立

(image - image_mean) / max(image_stddev, 1.0/sqrt(image_num_elements))

请记住,meanstandard deviation 应根据图像中的所有值进行计算。这意味着我们不需要指定计算它们的轴。

不使用 Tensorflow 的方法是使用 numpy,如下所示:

import math
import numpy as np
from PIL import Image

# open image
image = Image.open("your_image.jpg")
image = np.array(image)

# standardize image
mean = image.mean()
stddev = image.std()
adjusted_stddev = max(stddev, 1.0/math.sqrt(image.size))
standardized_image = (image - mean) / adjusted_stddev

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-29
    • 1970-01-01
    • 1970-01-01
    • 2013-09-25
    • 1970-01-01
    相关资源
    最近更新 更多