我想将每个 channel (R、G、B)的图像像素值标准化到范围 [0, 1]。
最小示例
#!/usr/bin/env python
import numpy as np
import scipy
from sklearn import preprocessing
original = scipy.misc.imread('Crocodylus-johnsoni-3.jpg')
scipy.misc.imshow(original)
transformed = np.zeros(original.shape, dtype=np.float64)
scaler = preprocessing.MinMaxScaler()
for channel in range(3):
transformed[:, :, channel] = scaler.fit_transform(original[:, :, channel])
scipy.misc.imsave("transformed.jpg", transformed)
发生了什么
服用https://commons.wikimedia.org/wiki/File:Crocodylus-johnsoni-3.jpg , 我得到以下“标准化”结果:
正如您所看到的,右侧有从上到下的线条。那里发生了什么?在我看来,正常化出了问题。如果是这样:我该如何解决它?
最佳答案
在 scikit-learn 中,形状为 (m, n) 的二维数组通常被解释为 m 个样本的集合,每个样本都有 >n 个功能。
MinMaxScaler.fit_transform()
转换每个特征,因此数组的每一列都独立于其他列进行转换。这会导致图像中出现垂直“条纹”。
看起来您打算独立缩放每个颜色 channel 。要使用 MinMaxScaler
实现此目的,请重新调整输入的形状,使每个 channel 变成一列。也就是说,如果原始图像的形状为 (m, n, 3),则在将其传递给 fit_transform()
方法之前将其 reshape 为 (m*n, 3),然后恢复形状创建转换后的数组的结果。
例如,
ascolumns = original.reshape(-1, 3)
t = scaler.fit_transform(ascolumns)
transformed = t.reshape(original.shape)
有了这个,转换
看起来像这样:
该图像看起来与原始图像一模一样,因为事实证明,在数组 original
中,每个 channel 的最小值和最大值分别为 0 和 255:
In [41]: original.min(axis=(0, 1))
Out[41]: array([0, 0, 0], dtype=uint8)
In [42]: original.max(axis=(0, 1))
Out[42]: array([255, 255, 255], dtype=uint8)
因此,在这种情况下,fit_transform 所做的就是将所有输入值统一转换为浮点范围 [0.0, 1.0]。如果其中一个 channel 的最小值或最大值不同,则变换后的图像看起来会有所不同。
<小时/>顺便说一句,使用纯 numpy 执行转换并不困难。 (我使用的是 Python 3,因此在下面,除法会自动将结果转换为浮点型。如果您使用的是 Python 2,则需要将参数之一转换为浮点型,或者使用 from __future__ 导入部门
。)
In [58]: omin = original.min(axis=(0, 1), keepdims=True)
In [59]: omax = original.max(axis=(0, 1), keepdims=True)
In [60]: xformed = (original - omin)/(omax - omin)
In [61]: np.allclose(xformed, transformed)
Out[61]: True
(该方法的一个潜在问题是,如果其中一个 channel 恒定,它将生成错误,因为这样 omax - omin
中的值之一将为 0。)
关于numpy - 为什么 MinMaxScaler 会向图像添加线条?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37751717/