在numpy中,如果我正在切换数组的末尾,有没有办法将填充条目归零,这样我得到的东西就像所需切片的大小一样?
例如,
>>> x = np.ones((3,3,))
>>> x
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])
>>> x[1:4, 1:4] # would behave as x[1:3, 1:3] by default
array([[ 1., 1., 0.],
[ 1., 1., 0.],
[ 0., 0., 0.]])
>>> x[-1:2, -1:2]
array([[ 0., 0., 0.],
[ 0., 1., 1.],
[ 0., 1., 1.]])
从视觉上看,我希望越界区域为零填充:
我正在处理图像,并希望将零填充表示为我的应用程序移动图像。
我目前的计划是在切片之前使用np.pad使整个数组更大,但索引似乎有点棘手。有没有更容易的方法?
答案 0 :(得分:4)
此类可以处理您的第一个测试(x[1:4, 1:4]
),并且可以修改以处理您的其他测试(即,如果您愿意,可以在开头添加零)。
class CustomArray():
def __init__(self, numpy_array):
self._array = numpy_array
def __getitem__(self, val):
# Get the shape you wish to return
required_shape = []
for i in range(2):
start = val[i].start
if not start:
start = 0
required_shape.append(val[i].stop - start)
get = self._array[val]
# Check first dimension
while get.shape[0] < required_shape[0]:
get = np.concatenate((get, np.zeros((1, get.shape[1]))))
# Check second dimension
get = get.T
while get.shape[0] < required_shape[1]:
get = np.concatenate((get, np.zeros((1, get.shape[1]))))
get = get.T
return get
以下是它的用法示例:
a = CustomArray(np.ones((3, 3)))
print(a[:2, :2])
[[ 1. 1.]
[ 1. 1.]]
print(a[:4, 1:6])
[[ 1. 1. 0. 0. 0.]
[ 1. 1. 0. 0. 0.]
[ 1. 1. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]]
# The actual numpy array is stored in the _array attribute
actual_numpy_array = a._array
答案 1 :(得分:4)
据我所知,对于这样的问题,没有任何笨拙的解决方案(也不是我知道的任何包装)。你可以自己做,但即使你只想要基本的切片,它也会是一个非常非常复杂的。我建议你手动np.pad
你的数组,然后在实际切片之前简单地偏移你的开始/停止/步骤。
但是,如果您需要支持的只是整数和切片而没有步骤,我会有一些&#34;工作代码&#34;为此:
import numpy as np
class FunArray(np.ndarray):
def __getitem__(self, item):
all_in_slices = []
pad = []
for dim in range(self.ndim):
# If the slice has no length then it's a single argument.
# If it's just an integer then we just return, this is
# needed for the representation to work properly
# If it's not then create a list containing None-slices
# for dim>=1 and continue down the loop
try:
len(item)
except TypeError:
if isinstance(item, int):
return super().__getitem__(item)
newitem = [slice(None)]*self.ndim
newitem[0] = item
item = newitem
# We're out of items, just append noop slices
if dim >= len(item):
all_in_slices.append(slice(0, self.shape[dim]))
pad.append((0, 0))
# We're dealing with an integer (no padding even if it's
# out of bounds)
if isinstance(item[dim], int):
all_in_slices.append(slice(item[dim], item[dim]+1))
pad.append((0, 0))
# Dealing with a slice, here it get's complicated, we need
# to correctly deal with None start/stop as well as with
# out-of-bound values and correct padding
elif isinstance(item[dim], slice):
# Placeholders for values
start, stop = 0, self.shape[dim]
this_pad = [0, 0]
if item[dim].start is None:
start = 0
else:
if item[dim].start < 0:
this_pad[0] = -item[dim].start
start = 0
else:
start = item[dim].start
if item[dim].stop is None:
stop = self.shape[dim]
else:
if item[dim].stop > self.shape[dim]:
this_pad[1] = item[dim].stop - self.shape[dim]
stop = self.shape[dim]
else:
stop = item[dim].stop
all_in_slices.append(slice(start, stop))
pad.append(tuple(this_pad))
# Let numpy deal with slicing
ret = super().__getitem__(tuple(all_in_slices))
# and padding
ret = np.pad(ret, tuple(pad), mode='constant', constant_values=0)
return ret
可以使用如下:
>>> x = np.arange(9).reshape(3, 3)
>>> x = x.view(FunArray)
>>> x[0:2]
array([[0, 1, 2],
[3, 4, 5]])
>>> x[-3:2]
array([[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 1, 2],
[3, 4, 5]])
>>> x[-3:2, 2]
array([[0],
[0],
[0],
[2],
[5]])
>>> x[-1:4, -1:4]
array([[0, 0, 0, 0, 0],
[0, 0, 1, 2, 0],
[0, 3, 4, 5, 0],
[0, 6, 7, 8, 0],
[0, 0, 0, 0, 0]])
请注意,这可能包含错误和&#34;没有干净编码&#34;部分,除了琐碎的情况外,我从未使用过它。
答案 2 :(得分:0)
如果是一维数组,我这样做,如果有人落在这里会很有用......
def getPaddedSlice(npArray, pos, lenSegment, center = False):
lenNpArray = len(npArray)
if center:
if lenSegment % 2 == 0:
startIndex = int(pos - math.floor(lenSegment / 2.0)) + 1
lastIndex = int(pos + math.ceil(lenSegment / 2.0)) + 1
else :
startIndex = int(pos - math.floor(lenSegment / 2.0))
lastIndex = int(pos + math.ceil(lenSegment / 2.0)) + 1
else:
startIndex = pos
lastIndex = startIndex + lenSegment
if startIndex < 0:
padded_slice = npArray[0: lastIndex]
padded_slice = np.concatenate((np.zeros(abs(startIndex)), padded_slice))
else:
if center :
padded_slice = npArray[startIndex: lastIndex]
else:
padded_slice = npArray[pos: lastIndex]
if lastIndex > len(npArray):
if center :
padded_slice = npArray[startIndex: pos + lenSegment]
padded_slice = np.concatenate((padded_slice, np.zeros(lastIndex - len(a))))
else :
padded_slice = npArray[pos: pos + lenSegment]
padded_slice = np.concatenate((padded_slice, np.zeros(lastIndex - len(a))))
return padded_slice
用法
a = np.asarray([2,2,3,1,7,6,5,4])
for i in range(len(a)):
b = getPaddedSlice(a, i, lenSegment, True)
print b
显示
[0 2 2 3]
[2 2 3 1]
[2 3 1 7]
[3 1 7 6]
[1 7 6 5]
[7 6 5 4]
[6 5 4 0]
[5 4 0 0]
答案 3 :(得分:0)
对于最简单的2或3级图像,下面是一个如何使用越界索引实现零填充“切片”的示例:
def padded_slice(src, sl):
output_shape = np.asarray(np.shape(src))
output_shape[0] = sl[1] - sl[0]
output_shape[1] = sl[3] - sl[2]
src = [max(sl[0], 0),
min(sl[1], img.shape[0]),
max(sl[2], 0),
min(sl[3], img.shape[1])]
dst = [src[0] - sl[0], src[1] - sl[0],
src[2] - sl[2], src[3] - sl[2]]
output = np.zeros(output_shape, dtype=img.dtype)
output[dst[0]:dst[1],dst[2]:dst[3]] = img[src[0]:src[1],src[2]:src[3]]
return output
例如,在100x100的图像上使用padded_slice(img, [-10,150,-10,150])
调用此函数,它将返回160x160的零填充图像。
答案 4 :(得分:0)
import numpy as np
def fill_crop(img, pos, crop):
'''
Fills `crop` with values from `img` at `pos`,
while accounting for the crop being off the edge of `img`.
*Note:* negative values in `pos` are interpreted as-is, not as "from the end".
'''
img_shape, pos, crop_shape = np.array(img.shape), np.array(pos), np.array(crop.shape),
end = pos+crop_shape
# Calculate crop slice positions
crop_low = np.clip(0 - pos, a_min=0, a_max=crop_shape)
crop_high = crop_shape - np.clip(end-img_shape, a_min=0, a_max=crop_shape)
crop_slices = (slice(low, high) for low, high in zip(crop_low, crop_high))
# Calculate img slice positions
pos = np.clip(pos, a_min=0, a_max=img_shape)
end = np.clip(end, a_min=0, a_max=img_shape)
img_slices = (slice(low, high) for low, high in zip(pos, end))
crop[tuple(crop_slices)] = img[tuple(img_slices)]
如果要考虑内存问题,那么将图像复制到填充版本可能不是很好。这对于高维输入也很有效,并且很清楚如何在需要时返回索引/切片。
要指示填充值,我们可以改用np.zeros
/ np.full
提前为农作物创建内存,然后填写所需的部分。这样,困难就不在找出要从何处复制,而是要粘贴到农作物内部的位置。
让我们看一维案例:
稍微想一想,就会发现:
crop_low
远高于0
,而pos
远低于0
,但是如果pos >= 0
,则crop_low == 0
crop_high
远低于crop.shape
,而end
远高于img.shape
,但是如果end <= img.shape
,则crop_high == crop.shape
如果我们将其放入普通的python代码中,它将看起来像这样:
crop_low = max(-pos, 0)
crop_high = crop.shape - max(end-img.shape, 0)
上面的其余代码仅用于索引。
# Examples in 1 dimension
img = np.arange(10, 20)
# Normal
pos = np.array([1,])
crop = np.full([5,], 0)
fill_crop(img, pos, crop)
assert crop.tolist() == [11, 12, 13, 14, 15]
# Off end
pos = np.array([8,])
crop = np.full([5,], 0)
fill_crop(img, pos, crop)
assert crop.tolist() == [18, 19, 0, 0, 0]
# Off start
pos = np.array([-2,])
crop = np.full([5,], 0)
fill_crop(img, pos, crop)
assert crop.tolist() == [ 0, 0, 10, 11, 12]
# Example in 2 dimensions (y,x)
img = np.arange(10, 10+10*10)\
.reshape([10, 10])
# Off Top right
pos = np.array([-2, 8])
crop = np.full([5, 5], 0)
fill_crop(img, pos, crop)
assert np.all(crop[:2] == 0) # That is, the top two rows are 0s
assert np.all(crop[:, 3:] == 0) # That is, the right 3 rows are 0s
assert np.all(crop[2:, :2] == img[:3, 8:])
# That is, the rows 2-5 and columns 0-1 in the crop
# are the same as the top 3 rows and columns 8 and 9 (the last two columns)
就在那里。对原始问题的过度设计的答案。