2 回答
TA贡献1797条经验 获得超4个赞
我曾经不得不解决一个类似的任务,所以我为它创建了以下函数。它允许为每个维度指定大小差异的分数,应该在之前和之后填充(类似于np.pad)。例如,如果您有两个形状数组(3,)和(5,),那么before=1将在左侧填充整个差异(在本例2中为 ),而在左侧填充before=0.5一个元素,在右侧填充一个元素。与np.pad这些因素类似,也可以为每个维度指定。这是实现:
import numpy as np
def pad_max_shape(arrays, before=None, after=1, value=0, tie_break=np.floor):
"""Pad the given arrays with a constant values such that their new shapes fit the biggest array.
Parameters
----------
arrays : sequence of arrays of the same rank
before, after : {float, sequence, array_like}
Similar to `np.pad -> pad_width` but specifies the fraction of values to be padded before
and after respectively for each of the arrays. Must be between 0 and 1.
If `before` is given then `after` is ignored.
value : scalar
The pad value.
tie_break : ufunc
The actual number of items to be padded _before_ is computed as the total number of elements
to be padded times the `before` fraction and the actual number of items to be padded _after_
is the remainder. This function determines how the fractional part of the `before` pad width
is treated. The actual `before` pad with is computed as ``tie_break(N * before).astype(int)``
where ``N`` is the total pad width. By default `tie_break` just takes the `np.floor` (i.e.
attributing the fraction part to the `after` pad width). The after pad width is computed as
``total_pad_width - before_pad_width``.
Returns
-------
padded_arrays : list of arrays
Notes
-----
By default the `before` pad width is computed as the floor of the `before` fraction times the number
of missing items for each axis. This is done regardless of whether `before` or `after` is provided
as a function input. For that reason the fractional part of the `before` pad width is attributed
to the `after` pad width (e.g. if the total pad width is 3 and the left fraction is 0.5 then the
`before` pad width is 1 and the `after` pad width is 2; in order to f). This behavior can be controlled
with the `tie_break` parameter.
"""
shapes = np.array([x.shape for x in arrays])
if before is not None:
before = np.zeros_like(shapes) + before
else:
before = np.ones_like(shapes) - after
max_size = shapes.max(axis=0, keepdims=True)
margin = (max_size - shapes)
pad_before = tie_break(margin * before.astype(float)).astype(int)
pad_after = margin - pad_before
pad = np.stack([pad_before, pad_after], axis=2)
return [np.pad(x, w, mode='constant', constant_values=value) for x, w in zip(arrays, pad)]
对于您的示例,您可以按如下方式使用它:
test = [np.ones(shape=(i, i, 3)) for i in range(5, 10)]
result = pad_max_shape(test, before=0.5, value=255)
print([x.shape for x in result])
print(result[0][:, :, 0])
这会产生以下输出:
[(9, 9, 3), (9, 9, 3), (9, 9, 3), (9, 9, 3), (9, 9, 3)]
[[255. 255. 255. 255. 255. 255. 255. 255. 255.]
[255. 255. 255. 255. 255. 255. 255. 255. 255.]
[255. 255. 1. 1. 1. 1. 1. 255. 255.]
[255. 255. 1. 1. 1. 1. 1. 255. 255.]
[255. 255. 1. 1. 1. 1. 1. 255. 255.]
[255. 255. 1. 1. 1. 1. 1. 255. 255.]
[255. 255. 1. 1. 1. 1. 1. 255. 255.]
[255. 255. 255. 255. 255. 255. 255. 255. 255.]
[255. 255. 255. 255. 255. 255. 255. 255. 255.]]
所以我们可以看到每个数组都被对称地填充到最大数组的形状(9, 9, 3)。
TA贡献1836条经验 获得超3个赞
的使用np.pad()实际上是有据可查的。
一个适用于您提供的数字的 3D 数据的示例是:
import numpy as np
arr = np.random.randint(0, 255, (72, 72, 3))
new_arr = np.pad(
arr, ((0, 92 - 72), (0, 92 - 72), (0, 0)),
'constant', constant_values=255)
print(new_arr.shape)
# (92, 92, 3)
编辑
要解决完整的问题,您需要首先确定最大尺寸,然后相应地填充所有其他图像。FlyingCircus包为您提供了许多功能,可以更轻松地完成工作(免责声明:我是它的主要作者)。
如果您可以将所有图像放入内存中,最简单的方法就是使用fc.extra.multi_reframe(),即:
import flyingcircus as fc
new_arrs = fc.extra.multi_reframe(arrs, background=255)
如果您无法将所有数据放入内存中,则应分两次执行此操作,一次计算适合所有输入的最小形状,然后执行实际填充fc.extra.reframe():
# assume your data is loaded with `load(filepath)`
# ... and saved with `save(array, filepath)`
# : first pass
shapes = [load(filepath).shape for filepath in filepaths]
target_shape = tuple(np.max(np.array(shapes), axis=0))
# : second pass
for filepath in filepaths:
arr = load(filepath)
new_arr = fc.extra.reframe(arr, target_shape, 0.5, 255)
save(new_arr, filepath)
在内部,fc.extra.reframe()正在使用np.pad()(或类似但更快的东西),它大致相当于:
def reframe(arr, target_shape, position=0.5, *args, **kws):
source_shape = arr.shape
padding = tuple(
(int(position * (dim_target - dim_source)),
(dim_target - dim_source) - int(position * (dim_target - dim_source)))
for dim_target, dim_source in zip(target_shape, source_shape))
return np.pad(arr, padding, *args, **kws)
reframe(arr, target_shape, 0.5, 'constant', constant_values=255)
请注意,position参数决定了数组相对于新形状的位置。的默认值0.5会将所有图像放在中心,而0.0或1.0会将其推到所有轴上新形状的一侧或另一侧。它的 FlyingCircus 版本更加灵活,您可以position分别为所有轴指定一个值。
添加回答
举报