将包含 11 位整数的字节数组转换为 16 位整数数组
Casting a bytearray containing 11-bit integers to an array of 16-bit integers
我有一个 bytes 对象或 bytearray 对象,表示 11 位整数的打包流。 (编辑:Stream 是没有填充的 11 位大端整数。)
是否有合理有效的方法将其复制到 16 位整数流?或者任何其他整数类型?
我知道 ctypes 支持位字段,但我不确定这是否对我有帮助。
我可以 "abuse" 标准库的某些部分已经为其他目的做了这样的位摆弄吗?
如果我必须求助于 cython,是否有可以处理可变位长的良好实现? IE。不仅适用于 11 位输入,还适用于 12、13 等?
编辑: 基于 PM2 Ring 回答的纯 python 解决方案
def unpackIntegers(data, num_points, bit_len):
"""Unpacks an array of integers of arbitrary bit-length into a
system-word aligned array of integers"""
# TODO: deal with native integer types separately for speedups
mask = (1 << bit_len) - 1
unpacked_bit_len = 2 ** ceil(log(bit_len, 2))
unpacked_byte_len = ceil(unpacked_bit_len / 8)
unpacked_array = bytearray(num_points * unpacked_byte_len)
unpacked = memoryview(unpacked_array).cast(
FORMAT_CODES[unpacked_byte_len])
num_blocks = num_points // 8
# Note: zipping generators is faster than calculating offsets
# from a block count
for idx1_start, idx1_stop, idx2_start, idx2_stop in zip(
range(0, num_blocks*bit_len, bit_len),
range(bit_len, (num_blocks+1)*bit_len, bit_len),
range(7, num_points, 8),
range(-1, num_points-8, 8),
):
n = int.from_bytes(data[idx1_start:idx1_stop], 'big')
for i in range(idx2_start, idx2_stop, -1):
unpacked[i] = n & mask
n >>= bit_len
# process left-over part (missing from PM2 Ring's answer)
else:
points_left = num_points % 8
bits_left = points_left * bit_len
bytes_left = len(data)-num_blocks*bit_len
num_unused_bits = bytes_left * 8 - bits_left
n = int.from_bytes(data[num_blocks*bit_len:], 'big')
n >>= num_unused_bits
for i in range(num_points-1, num_points-points_left-1, -1):
unpacked[i] = n & mask
n >>= bit_len
return unpacked
使用第 3 方库可能有更有效的方法,但这是使用标准 Python 的一种方法。
unpack
生成器以块的形式迭代其 data
参数,data
可以是任何产生字节的可迭代对象。为了解压缩 11 位数据,我们读取 11 字节的数据块,将这些字节组合成一个整数,然后将该整数分成 8 块,因此每块将包含来自相应 11 个源位的数据。
def unpack(data, bitlen):
mask = (1 << bitlen) - 1
for chunk in zip(*[iter(data)] * bitlen):
n = int.from_bytes(chunk, 'big')
a = []
for i in range(8):
a.append(n & mask)
n >>= bitlen
yield from reversed(a)
# Test
# 0 to 23 in 11 bit integers, packed into bytes
data = bytes([
0, 0, 4, 1, 0, 48, 8, 1, 64, 48, 7,
1, 0, 36, 5, 0, 176, 24, 3, 64, 112, 15,
2, 0, 68, 9, 1, 48, 40, 5, 64, 176, 23,
])
print(list(unpack(data, 11)))
输出
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
请注意,如果 data
不包含 bitlen
字节的倍数,那么它将以将被忽略的部分块结束。
我有一个 bytes 对象或 bytearray 对象,表示 11 位整数的打包流。 (编辑:Stream 是没有填充的 11 位大端整数。)
是否有合理有效的方法将其复制到 16 位整数流?或者任何其他整数类型?
我知道 ctypes 支持位字段,但我不确定这是否对我有帮助。
我可以 "abuse" 标准库的某些部分已经为其他目的做了这样的位摆弄吗?
如果我必须求助于 cython,是否有可以处理可变位长的良好实现? IE。不仅适用于 11 位输入,还适用于 12、13 等?
编辑: 基于 PM2 Ring 回答的纯 python 解决方案
def unpackIntegers(data, num_points, bit_len):
"""Unpacks an array of integers of arbitrary bit-length into a
system-word aligned array of integers"""
# TODO: deal with native integer types separately for speedups
mask = (1 << bit_len) - 1
unpacked_bit_len = 2 ** ceil(log(bit_len, 2))
unpacked_byte_len = ceil(unpacked_bit_len / 8)
unpacked_array = bytearray(num_points * unpacked_byte_len)
unpacked = memoryview(unpacked_array).cast(
FORMAT_CODES[unpacked_byte_len])
num_blocks = num_points // 8
# Note: zipping generators is faster than calculating offsets
# from a block count
for idx1_start, idx1_stop, idx2_start, idx2_stop in zip(
range(0, num_blocks*bit_len, bit_len),
range(bit_len, (num_blocks+1)*bit_len, bit_len),
range(7, num_points, 8),
range(-1, num_points-8, 8),
):
n = int.from_bytes(data[idx1_start:idx1_stop], 'big')
for i in range(idx2_start, idx2_stop, -1):
unpacked[i] = n & mask
n >>= bit_len
# process left-over part (missing from PM2 Ring's answer)
else:
points_left = num_points % 8
bits_left = points_left * bit_len
bytes_left = len(data)-num_blocks*bit_len
num_unused_bits = bytes_left * 8 - bits_left
n = int.from_bytes(data[num_blocks*bit_len:], 'big')
n >>= num_unused_bits
for i in range(num_points-1, num_points-points_left-1, -1):
unpacked[i] = n & mask
n >>= bit_len
return unpacked
使用第 3 方库可能有更有效的方法,但这是使用标准 Python 的一种方法。
unpack
生成器以块的形式迭代其 data
参数,data
可以是任何产生字节的可迭代对象。为了解压缩 11 位数据,我们读取 11 字节的数据块,将这些字节组合成一个整数,然后将该整数分成 8 块,因此每块将包含来自相应 11 个源位的数据。
def unpack(data, bitlen):
mask = (1 << bitlen) - 1
for chunk in zip(*[iter(data)] * bitlen):
n = int.from_bytes(chunk, 'big')
a = []
for i in range(8):
a.append(n & mask)
n >>= bitlen
yield from reversed(a)
# Test
# 0 to 23 in 11 bit integers, packed into bytes
data = bytes([
0, 0, 4, 1, 0, 48, 8, 1, 64, 48, 7,
1, 0, 36, 5, 0, 176, 24, 3, 64, 112, 15,
2, 0, 68, 9, 1, 48, 40, 5, 64, 176, 23,
])
print(list(unpack(data, 11)))
输出
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
请注意,如果 data
不包含 bitlen
字节的倍数,那么它将以将被忽略的部分块结束。