通过引用组合NumPy数组

4
我希望将两个数组合并成一个新的数组,并在O(1)时间内完成。然后,我想更改此新数组中的值以更改旧数组中的值。
这是在PyGame的surfarray模块的背景下,该模块没有返回RGBA(n * m * 4)数组的函数 - 只有RGB(n * m * 3)和A(n * m)数组分别存在。理想情况下,我将创建一个新数组“RGBA”,它在O(1)中引用“RGB”和“A”数组。更改“RGBA”会更改“RGB”和“A”,从而更改表面。
我不知道是否可能,因为我无法想出使用C完成它的方法。但是,我可以想到一些想法。例如,也许有一种numpy数组类型,封装了一些子数组,并在C级别上将索引重定向到正确的子数组。这将是很好的。

我所知道的唯一方法是子类化ndarray - 请参阅http://docs.scipy.org/doc/numpy/user/basics.subclassing.html - Andrew Marshall
如果你所说的RGBA是指一段连续的内存块,而起始于两个独立的块(RGB和A由PyGame返回),那么没有办法在O(1)时间内完成这个操作。但是为什么需要将RGBA作为单个数组呢?或者,你能否从一个连续的RGBA块开始,并将其分成两部分(RGB和A)交给PyGame? - tom10
2
.reshape创建一个新的数组,所以我使用.resize代替。实际上更难了,因为pixels2D返回的像素是一列数组,而不是相反的。无论如何,对于未来的人们,创建BGRA数组的代码是:http://pastebin.com/KLgsFFrU。把它变成RGBA会很好,也许有一种方法可以得到一个视图? - geometrian
@IanMallett 在使用 uint8 视图之前,尝试使用 dtype'<u4''>u4' 的反向字节序的原始数组视图。除非您复制了该数组,否则它可能无法正常工作,但是尝试一下可能值得。 - Jaime
似乎没有任何影响。但是反转字节序会使其成为ARGB吗? - geometrian
显示剩余4条评论
2个回答

1
您可以创建自己的类来管理引用过程,如下例所示。您应该进一步完善此功能,包括使用__getslice____add____mul__等实现切片功能。
import numpy as np
a1 = np.arange(1,11)
a2 = np.arange(101,111)
class Combiner():
    def __init__(self, *args):
        self.arrays = [arg for arg in args]
        self.lens = [len(arg) for arg in args]
    def __getitem__(self,i):
        if i >=0:
            shift = 0
            acc = 0
            for j,l in enumerate(self.lens):
                acc += l
                if i<acc:
                    return self.arrays[j][i-shift]
                shift += l
        if i<0:
            shift = 0
            acc = 0
            for j in xrange(len(self.lens)-1,-1,-1):
                l = self.lens[j]
                acc -= l
                if i>=acc:
                    return self.arrays[j][i+shift]
                shift += l

    def __setitem__(self,i,v):
        if i >=0:
            shift = 0
            acc = 0.
            for j,l in enumerate(self.lens):
                acc += l
                if i<acc:
                    self.arrays[j][i-shift] = v
                    return
                shift += l
        if i<0:
            shift = 0
            acc = 0
            for j in xrange(len(self.lens)-1,-1,-1):
                l = self.lens[j]
                acc -= l
                if i>=acc:
                    self.arrays[j][i+shift] = v
                    return
                shift += l

a3 = Combiner(a1,a2)
print a3[-10]
# 101
a3[-2] = 22222
a3[ 4] = 11111
print a1
#[    1     2     3     4 11111     6     7     8     9    10]
print a2
#[  101   102   103   104   105   106   107   108 22222   110]

1
我认为我当时没有接受这个答案的原因是它不够优雅,我希望有一个更符合Python语法的答案。当然,在内部它必须做类似于这样的事情。 - geometrian

0

我不确定我完全理解了这个问题。根据我的了解,如果您像这样初始化pygame和表面(每行注意“32”),那么pygame表面将是RGBA(确切地说是BGRA)连续数组:

# display surface
DISP = pygame.display.set_mode((window_w, window_h), 0, 32)
# some surface
window = pygame.Surface((w, h), 0, 32) 

我建议尽可能使用32位而不是24位,因为24位表面很难与数组互操作(我使用numpy来存储和操作图像数据)。例如,如果我没有弄错的话,24位表面必须具有可被4整除的像素数量。

正如所说,我不确定您的最终任务是什么,但以下是我的做法。例如,使用openCV库加载图像并将其转换为BGRA数组:

# BGR 3d numpy array array (shape = h, w, 3) with image data
img = cv2.imread(filename)
# BGRA 3d numpy array (shape = h, w, 4) 
img = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)

然后这个函数将整个数组数据复制到一个表面上,只需要确保它们具有相同的像素大小:

# copy array data into a pygame surface
def copy_arr(surface, myarray):
    bv = surface.get_buffer()
    bv.write(myarray.tostring(), 0)

但可能你想做其他的事情。


这个问题很老了,我也不记得当时想做什么了。看起来是要在两个数组周围创建一个RGBA包装器——一个是RGB,另一个是A。所以这并没有真正帮助到你。 - geometrian
@imallett 好的,算了。我只是觉得你可能把一些简单的任务复杂化了。通常,如果需要操作像素数据,最好将数据存储在数组中,而不是从表面创建引用。表面更适合于pygame自己的函数,如rotozoom等。 - Mikhail V
没错,问题在于我有两个单独的数组,而不是一个。顺便说一下:表面实际上就是数组。这就是像素数组所做的事情:公开后备存储器。 - geometrian

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