Numpy blit(将数组的一部分复制到另一个不同大小的数组)

4
我希望将一个数组复制到另一个具有不同大小的数组。 我需要以下这个函数:
blit(destimg,src,dstlocation)

例如:blit(zeros((7,7)),ones((3,3)),(4,4)) 执行后会得到以下结果:
array([[ 0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  1.,  1.,  1.],
       [ 0.,  0.,  0.,  0.,  1.,  1.,  1.],
       [ 0.,  0.,  0.,  0.,  1.,  1.,  1.]])

数组 src 的左上中心现在位于数组 destimg 的位置 (4,4)

如果我执行 blit(zeros((7,7)),ones((3,3)),(5,5)),我将得到:

array([[ 0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  1.,  1.],
       [ 0.,  0.,  0.,  0.,  0.,  1.,  1.]])

src数组不适合destimg,但其左上角仍位于正确的位置。


2
你不能通过简单的赋值(即“=”运算符)和使用适当的索引来实现你想要的吗? - Dr. Jan-Philip Gehrcke
@Jan-PhilipGehrcke 我可以这样做,但是对于源数组不适合的情况,我需要进行一些计算来确定正确的索引。我想知道numpy是否已经有了这样的函数。 - João Abrantes
为什么你的blit函数不够用? - wwii
1
也就是说,目标数组的整个或部分将被源数组的整个或部分替换。因此,问题归结为找到索引,对吧。这是否仅适用于二维数组?还是应该适用于n维数组? - Dr. Jan-Philip Gehrcke
3个回答

4
你可以计算出适当的切片:
import numpy as np

def blit(dest, src, loc):
    pos = [i if i >= 0 else None for i in loc]
    neg = [-i if i < 0 else None for i in loc]
    target = dest[[slice(i,None) for i in pos]]
    src = src[[slice(i, j) for i,j in zip(neg, target.shape)]]
    target[[slice(None, i) for i in src.shape]] = src
    return dest

print(blit(np.zeros((7,7)), np.ones((3,3)), (5, 5)))

产生的结果
[[ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  1.  1.]
 [ 0.  0.  0.  0.  0.  1.  1.]]

并且。
print(blit(np.zeros((7,7)), np.ones((3,3)), (-1, -1)))

产量
[[ 1.  1.  0.  0.  0.  0.  0.]
 [ 1.  1.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]]

0

我找到了一个解决方案(有点啰嗦),肯定有更优雅的方法,但在这段时间内这个方法可以使用。

from numpy import *

def blit(dest, src, loc):
    th,tw=dest.shape
    sh,sw=src.shape
    sr = 0 if -loc[0]<0 else -loc[0]
    fr = sh if loc[0]+sh<=th else sh-(loc[0]+sh-th)
    sc = 0 if -loc[1]<0 else -loc[1]
    fc = sw if loc[1]+sw<=tw else sw-(loc[1]+sw-th)
    loc[0] = max(0,loc[0])
    loc[1] = max(0,loc[1])  
    dest[loc[0]:loc[0]+sh-sr,loc[1]:loc[1]+sw-sc] = src[sr:fr,sc:fc]

dest = zeros((7,7))
src = ones((3,3))
loc = [5,5]
blit(dest, src, loc)
print dest

产生:

[[ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  1.  1.]
 [ 0.  0.  0.  0.  0.  1.  1.]]

dest = zeros((7,7))
src = ones((3,3))
loc = [-1,-1]
blit(dest, src, loc)
print dest

产生

[[ 1.  1.  0.  0.  0.  0.  0.]
 [ 1.  1.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.]]

正如您所看到的,@unutbu提出了一种更好的方法。您绝对应该利用numpy数组上的切片索引行为类似于内置类型Python切片的事实,特别是对于超出边界的索引。引用自Python字符串介绍:“退化的切片索引会被优雅地处理:一个太大的下标会被替换为字符串大小,上限比下限小则返回一个空字符串。” - Dr. Jan-Philip Gehrcke

0

这是我的实现:

def blit(a, b, offsets=(0,), as_shapes=False):
    """
    Computes the slices of the overlapping regions of arrays <a> and <b>. If offsets are specified, 
    <b> will be shifted by these offsets before computing the overlap.

    Example:
          50
       ┌──────┐
       │      │ 
     65│  ┌───┼────┐
       │  │   │    │50
       └──┼───┘    │
          └────────┘
              55
    <a> is the 65x50 array and <b> is the 50x55 array. The offsets are (32, 18). The returned 
    slices are [32:65, 18:50] for <a> and [0:33, 0:32] for <b>.

    Arrays of different dimensions can be used (e.g. 3-dimensional RGB image and 2-dimensional 
    grayscale image) but the slices will only go up to min(a.ndim, b.ndim). An offset with more 
    elements than that will throw a ValueException.

    Instead of arrays, shapes can be directly passed to the function by setting as_shapes to True.

    :param a: an array object or a tuple is as_shape is True
    :param b: an array object or a tuple is as_shape is True
    :param offsets: a sequence of offsets
    :param as_shapes: if True, <a> and <b> are expected to be array shapes rather than array
    :return: a multidimensional slice for <a> followed by a multidimensional slice for <b>
    """

    # Retrieve and check the array shapes and offset
    if not as_shapes:
        a, b = np.array(a, copy=False), np.array(b, copy=False)
        a_shape, b_shape = a.shape, b.shape
    else:
        a_shape, b_shape = a, b
    n = min(len(a_shape), len(b_shape))
    if n == 0:
        raise ValueError("Cannot overlap with an empty array")
    offsets = tuple(offsets) + (0,) * (n - len(offsets))
    if len(offsets) > n:
        raise ValueError("Offset has more elements than either number of dimensions of the arrays")

    # Compute the slices
    a_slices, b_slices = [], []
    for i, (a_size, b_size, offset) in enumerate(zip(a_shape, b_shape, offsets)):
        a_min = max(0, offset)
        a_max = min(a_size, max(b_size + offset, 0))
        b_min = max(0, -offset)
        b_max = min(b_size, max(a_size - offset, 0))
        a_slices.append(slice(a_min, a_max))
        b_slices.append(slice(b_min, b_max))

    return tuple(a_slices), tuple(b_slices) 

def paste(a, b, offsets=(0,), copy=True):
    """
    Pastes array <b> into array <a> at position <offsets>

    :param a: an array object
    :param b: an array object
    :param offsets: the position in <a> at which <b> is to be pasted
    :param copy: whether to paste <b> in <a> or in a copy of <a>
    :return: either <a> or a copy of <a> with <b> pasted on it
    """

    out = np.array(a, copy=copy)
    a_slice, b_slice = blit(a, b, offsets)
    out[a_slice] = b[b_slice]
    return out

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接