【发布时间】:2014-02-26 23:07:15
【问题描述】:
我正在尝试调整给定因子的 2D numpy 数组的大小,从而在输出中获得更小的数组。
数组是从图像文件中读取的,其中一些值应该是 NaN(不是数字,来自 numpy 的 np.nan):它是卫星遥感测量的结果,只是没有测量一些像素。
为此我找到的合适的包是 scypy.misc.imresize,但是输出数组中包含 NaN 的每个像素都设置为 NaN,即使原始像素插值在一起有一些有效数据也是如此。
我的解决方案附在这里,我所做的基本上是:
- 根据原始阵列形状和所需的缩减系数创建一个新阵列
- 创建一个索引数组来寻址原始数组的所有像素,以便为新数组中的每个像素进行平均
- 循环遍历新的阵列像素并平均所有非NaN像素以获得新的阵列像素值;如果只有 NaN,则输出将为 NaN。
我打算在不同的输出(输入像素的平均值、中值、标准差等)之间添加关键字来选择。
它按预期工作,但在 ~1Mpx 图像上大约需要 3 秒。由于我缺乏 python 经验,我正在寻找改进。
有没有人建议如何更好、更有效地做到这一点?
有人知道已经实现了所有这些东西的库吗?
谢谢。
这里有一个使用下面的代码生成的随机像素输入的示例输出:
import numpy as np
import pylab as plt
from scipy import misc
def resize_2d_nonan(array,factor):
"""
Resize a 2D array by different factor on two axis sipping NaN values.
If a new pixel contains only NaN, it will be set to NaN
Parameters
----------
array : 2D np array
factor : int or tuple. If int x and y factor wil be the same
Returns
-------
array : 2D np array scaled by factor
Created on Mon Jan 27 15:21:25 2014
@author: damo_ma
"""
xsize, ysize = array.shape
if isinstance(factor,int):
factor_x = factor
factor_y = factor
elif isinstance(factor,tuple):
factor_x , factor_y = factor[0], factor[1]
else:
raise NameError('Factor must be a tuple (x,y) or an integer')
if not (xsize %factor_x == 0 or ysize % factor_y == 0) :
raise NameError('Factors must be intger multiple of array shape')
new_xsize, new_ysize = xsize/factor_x, ysize/factor_y
new_array = np.empty([new_xsize, new_ysize])
new_array[:] = np.nan # this saves us an assignment in the loop below
# submatrix indexes : is the average box on the original matrix
subrow, subcol = np.indices((factor_x, factor_y))
# new matrix indexs
row, col = np.indices((new_xsize, new_ysize))
# some output for testing
#for i, j, ind in zip(row.reshape(-1), col.reshape(-1),range(row.size)) :
# print '----------------------------------------------'
# print 'i: %i, j: %i, ind: %i ' % (i, j, ind)
# print 'subrow+i*new_ysize, subcol+j*new_xsize :'
# print i,'*',new_xsize,'=',i*factor_x
# print j,'*',new_ysize,'=',j*factor_y
# print subrow+i*factor_x,subcol+j*factor_y
# print '---'
# print 'array[subrow+i*factor_x,subcol+j*factor_y] : '
# print array[subrow+i*factor_x,subcol+j*factor_y]
for i, j, ind in zip(row.reshape(-1), col.reshape(-1),range(row.size)) :
# define the small sub_matrix as view of input matrix subset
sub_matrix = array[subrow+i*factor_x,subcol+j*factor_y]
# modified from any(a) and all(a) to a.any() and a.all()
# see https://stackoverflow.com/a/10063039/1435167
if not (np.isnan(sub_matrix)).all(): # if we haven't all NaN
if (np.isnan(sub_matrix)).any(): # if we haven no NaN at all
msub_matrix = np.ma.masked_array(sub_matrix,np.isnan(sub_matrix))
(new_array.reshape(-1))[ind] = np.mean(msub_matrix)
else: # if we haven some NaN
(new_array.reshape(-1))[ind] = np.mean(sub_matrix)
# the case assign NaN if we have all NaN is missing due
# to the standard values of new_array
return new_array
row , cols = 6, 4
a = 10*np.random.random_sample((row , cols))
a[0:3,0:2] = np.nan
a[0,2] = np.nan
factor_x = 2
factor_y = 2
a_misc = misc.imresize(a, .5, interp='nearest', mode='F')
a_2d_nonan = resize_2d_nonan(a,(factor_x,factor_y))
print a
print
print a_misc
print
print a_2d_nonan
plt.subplot(131)
plt.imshow(a,interpolation='nearest')
plt.title('original')
plt.xticks(arange(a.shape[1]))
plt.yticks(arange(a.shape[0]))
plt.subplot(132)
plt.imshow(a_misc,interpolation='nearest')
plt.title('scipy.misc')
plt.xticks(arange(a_misc.shape[1]))
plt.yticks(arange(a_misc.shape[0]))
plt.subplot(133)
plt.imshow(a_2d_nonan,interpolation='nearest')
plt.title('my.func')
plt.xticks(arange(a_2d_nonan.shape[1]))
plt.yticks(arange(a_2d_nonan.shape[0]))
编辑
我对地址ChrisProsser comment添加了一些修改。
如果我用其他值替换 NaN,比如说非 NaN 像素的平均值,它将影响所有后续计算:重新采样的原始数组与替换为 NaN 的重新采样数组之间的差异表明 2 个像素改变了他们的价值观。
我的目标只是跳过所有的 NaN 像素。
# substitute NaN with the average value
ind_nonan , ind_nan = np.where(np.isnan(a) == False), np.where(np.isnan(a) == True)
a_substitute = np.copy(a)
a_substitute[ind_nan] = np.mean(a_substitute[ind_nonan]) # substitute the NaN with average on the not-Nan
a_substitute_misc = misc.imresize(a_substitute, .5, interp='nearest', mode='F')
a_substitute_2d_nonan = resize_2d_nonan(a_substitute,(factor_x,factor_y))
print a_2d_nonan-a_substitute_2d_nonan
[[ nan -0.02296697]
[ 0.23143208 0. ]
[ 0. 0. ]]
** 第二次编辑**
为了解决Hooked 的答案,我添加了一些额外的代码。这是一个有趣的想法,遗憾的是它在应该是“空”(NaN)的像素上插入了新值,并且对于我的小例子,生成的 NaN 比好的值更多。
X , Y = np.indices((row , cols))
X_new , Y_new = np.indices((row/factor_x , cols/factor_y))
from scipy.interpolate import CloughTocher2DInterpolator as intp
C = intp((X[ind_nonan],Y[ind_nonan]),a[ind_nonan])
a_interp = C(X_new , Y_new)
print a
print
print a_interp
[[ nan, nan],
[ nan, nan],
[ nan, 6.32826577]])
【问题讨论】:
-
一个 2x2 单元格/窗口有一个 Nan,你期待其他三个的平均值吗?
-
如果一个单元格/窗口中的所有值都是 NaN,您对该单元格的值有何期望?