【问题标题】:How can I pack numpy bool arrays into a string of bits?如何将 numpy bool 数组打包成一串位?
【发布时间】:2015-04-27 22:40:53
【问题描述】:

概述

在 numpy 中,我有一个布尔数组。该数组是从图像中检索的;它是二维的,包含 1024 列和 768 行。我想通过以太网电缆推送这些数据。有多种方法可以做到这一点,但就我而言,速度非常关键,因此内存也非常关键。

由于每个数组中有1024 x 768 = 786432 元素(像素),并且每个元素要么是True 要么是False,因此理论上可以将数组打包成98,304 个未压缩字节或96 KB。

786432 bits / 8 bits per byte =         98304 bytes
98304 bytes / 1024 bytes per kilobyte = 96    kilobytes

这需要展平数组

[ [True, False, True, ..., True]
  [False, True, True, ..., True]
  ...
  [True, True, False, ..., False] ]

# flatten the array

[True, False, True, ..., False]

理论上可以表示为字节的位,因为 786,432 位均匀地适合 98,304 字节;每个数组应该能够由 98,304 个八位字符表示。

问题

如何通过以太网快速发送 1024×768 bool numpy 数组?我正在研究 bitstring python 库,但我不确定如何快速将 numpy 数组通过管道传输到 bitstring 类中。

其他信息/问题

具体来说,我将这些数组从 Raspberry Pi 2 发送到常规 Raspberry Pi。

  1. socketSOCK_STREAM 是最快的解决方法吗?
  2. 鉴于 RPis 计算能力,压缩和解压缩数组会更快吗?如果是这样,压缩必须是无损的。
  3. 我已经研究过序列化 numpy 数组而不是使用 bitstring 的东西,但是腌制的对象太大而无法通过 SOCK_STREAM 发送。我对socket 的东西做错了吗?

我的代码/解决方案 [已解决]

客户

import socket
from scipy.misc import imread
import numpy

IP = '127.0.0.1'
PORT = 7071
ADDRESS = (IP, PORT)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

image = imread('input.png')[:,:,[2]]
image[image < 170] = 0
image[image != 0] = 1
image = numpy.reshape(image, (-1, 1))
image = numpy.packbits(image)
data = image.tostring()

sock.connect(ADDRESS)
for i in range(0, 93804, 1024):
    sock.send(data[i:i+1024])
sock.shutdown(socket.SHUT_WR)
sock.close()

服务器

import socket
from scipy.misc import imsave
import numpy

IP = '127.0.0.1'
PORT = 7071
ADDRESS = (IP, PORT)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(ADDRESS)
sock.listen(1)

while True:
    c, addr = sock.accept()
    data = ''
    package = c.recv(1024)
    while package:
        data += package
        package = c.recv(1024)
    image = numpy.fromstring(data, dtype=numpy.uint8)
    image = numpy.unpackbits(image)
    image = numpy.reshape(image, (-1, 768))
    imsave('output.png', image)
    c.close()
sock.close()

如您所见,我通过一系列 1024 字节数据包通过 TCP/SOCK_STREAM 结束了每个数组。

【问题讨论】:

    标签: python arrays numpy pickle


    【解决方案1】:

    您可以使用np.packbitsnp.bool 数组的内容打包到大小为1/8 的np.uint8 数组中,这样每个“打包”布尔元素只使用一个位。可以使用np.unpackbits恢复原始数组。

    import numpy as np
    
    x = array([0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1], dtype=np.bool)
    
    print(x.itemsize, x.nbytes)
    # (1, 16)
    
    xp = np.packbits(x)
    print(xp)
    # [ 24 139]
    
    print(xp.itemsize, xp.nbytes)
    # (1, 2)
    
    print(np.unpackbits(xp))
    # [0 0 0 1 1 0 0 0 1 0 0 0 1 0 1 1]
    

    从这里开始最明显的方法是将打包数组序列化为原始字节字符串pipe it through a UDP socket,然后对其进行反序列化并在另一端解包。 numpy 的原生序列化(.tostring()np.fromstring())可能会比使用 picklecPickle 快得多。

    如果您想尝试压缩,一种选择是使用本机 zlib 模块在通过管道传递字节字符串之前对其进行压缩,然后在另一侧对其进行解压缩。您是否从中看到任何好处将在很大程度上取决于您的输入数组的可压缩程度,以及执行压缩/解压缩的硬件。

    【讨论】:

    • 我最初是遵循这个思路,但后来我陷入了 UDP 问题。我的数组大于任何 UDP 数据包的最大理论大小。我怎样才能优雅地处理这么大的数组(理想情况下我想用一两行 Python 代码发送它)?我是否必须将其分解成更小的数据包并使用 TCP/SOCK_STREAM,或者我可以以某种方式一次发送大量字节?
    • @user3745189 这似乎值得提出一个新问题。
    • @user3745189 我赞同马克的评论。如果您使用一组更合适的标签单独发布问题,您更有可能在问题的网络部分获得帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-07
    • 1970-01-01
    • 2016-04-11
    • 1970-01-01
    • 2018-12-20
    • 2013-08-29
    相关资源
    最近更新 更多