为了账号安全,请及时绑定邮箱和手机立即绑定

如何将多个图像填充到包含它们的最小形状?

如何将多个图像填充到包含它们的最小形状?

守着星空守着你 2022-06-07 17:59:04
我正在尝试编写一个函数来加载和处理 NN 的数据。作为输入,我有一组不同尺寸的图片。图片应表示为具有 RGB 通道的 3D numpy 数组。我需要它们具有相同的大小(最大图片的大小)。我已经尝试过np.pad,但似乎我不知道它应该如何工作。实际上,即使我得到了填充,我也不知道如何根据图片的大小来改变它。这是代码:from PIL import Imageimport numpy as npimport cv2import osdef load_data(path):    aminoacids = ['Ala','Arg','Asn','Asp','Cys','Gln','Glu','Gly','His','Ile', 'Ini', 'Leu','Lys','Met','Phe','Pro','Pyr', 'Sec','Ser','Thr','Trp','Tyr','Val']    matrix = []    answer_labeled = []    names = os.listdir(path)    for i in names:      matrix = cv2.imread(path + i, 1)      matrix = np.pad(matrix, (0, 1), 'constant', constant_values=[255,255,255])      for y in aminoacids:        if y + "-" in i:          a = [matrix, y]          answer_labeled.append(a)    return answer_labeleddata_processed = load_data("/content/drive/My Drive/Thesis/dl/img/ans/")我收到此错误:ValueErrorTraceback (most recent call last)<ipython-input-50-e021738e59ea> in <module>()     20     return answer_labeled     21 ---> 22 data_processed = load_data("/content/drive/My Drive/Thesis/dl/img/ans/")     23      24 # print(len(os.listdir("/content/drive/My Drive/Thesis/dl/img/ans/")))<ipython-input-50-e021738e59ea> in load_data(path)     13     for i in names:     14       matrix = cv2.imread(path + i, 1)---> 15       matrix = np.pad(matrix, (0, 1), 'constant', constant_values=[255,255,255])     16       for y in aminoacids:     17         if y + "-" in i:/usr/local/lib/python2.7/dist-packages/numpy/lib/arraypad.pyc in pad(array, pad_width, mode, **kwargs)   1208                 kwargs[i] = _as_pairs(kwargs[i], narray.ndim, as_index=True)   1209             if i in ['end_values', 'constant_values']:-> 1210                 kwargs[i] = _as_pairs(kwargs[i], narray.ndim)   1211     else:   1212         # Drop back to old, slower np.apply_along_axis mode for user-supplied当然,我试图用谷歌搜索这个错误,但没有找到对我有用或可以理解的东西(因为我真的是编程新手)。我将不胜感激任何帮助和想法。
查看完整描述

2 回答

?
繁星coding

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)。


查看完整回答
反对 回复 2022-06-07
?
米脂

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分别为所有轴指定一个值。


查看完整回答
反对 回复 2022-06-07
  • 2 回答
  • 0 关注
  • 131 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信