我有一个包含10位整数流的二进制文件。我想阅读它并将值存储在列表中。
它正在使用以下代码,该代码读取my_file
并使用整数值填充pixels
:
file = open("my_file", "rb")
pixels = []
new10bitsByte = ""
try:
byte = file.read(1)
while byte:
bits = bin(ord(byte))[2:].rjust(8, '0')
for bit in reversed(bits):
new10bitsByte += bit
if len(new10bitsByte) == 10:
pixels.append(int(new10bitsByte[::-1], 2))
new10bitsByte = ""
byte = file.read(1)
finally:
file.close()
将字节读入位并将其读回“10位”字节似乎并不优雅。有没有更好的方法呢?
使用8位或16位整数,我可以使用file.read(size)
并将结果直接转换为int。但是在这里,因为每个值都以1.25字节存储,所以我需要像file.read(1.25)
...
答案 0 :(得分:2)
这是一个在不使用文本字符串转换的情况下执行位操作的生成器。希望它更有效率。 :)
为了测试它,我将范围(1024)中的所有数字写入BytesIO流,其行为类似于二进制文件。
from io import BytesIO
def tenbitread(f):
''' Generate 10 bit (unsigned) integers from a binary file '''
while True:
b = f.read(5)
if len(b) == 0:
break
n = int.from_bytes(b, 'big')
#Split n into 4 10 bit integers
t = []
for i in range(4):
t.append(n & 0x3ff)
n >>= 10
yield from reversed(t)
# Make some test data: all the integers in range(1024),
# and save it to a byte stream
buff = BytesIO()
maxi = 1024
n = 0
for i in range(maxi):
n = (n << 10) | i
#Convert the 40 bit integer to 5 bytes & write them
if i % 4 == 3:
buff.write(n.to_bytes(5, 'big'))
n = 0
# Rewind the stream so we can read from it
buff.seek(0)
# Read the data in 10 bit chunks
a = list(tenbitread(buff))
# Check it
print(a == list(range(maxi)))
<强>输出强>
True
执行list(tenbitread(buff))
是将生成器输出转换为列表的最简单方法,但您可以轻松地迭代值,例如
for v in tenbitread(buff):
或
for i, v in enumerate(tenbitread(buff)):
如果你想要索引以及数据值。
这是生成器的小端版本,它提供与代码相同的结果。
def tenbitread(f):
''' Generate 10 bit (unsigned) integers from a binary file '''
while True:
b = f.read(5)
if not len(b):
break
n = int.from_bytes(b, 'little')
#Split n into 4 10 bit integers
for i in range(4):
yield n & 0x3ff
n >>= 10
我们可以通过“un-rolling”for循环来略微改进这个版本,这可以让我们摆脱最终的掩蔽和转移操作。
def tenbitread(f):
''' Generate 10 bit (unsigned) integers from a binary file '''
while True:
b = f.read(5)
if not len(b):
break
n = int.from_bytes(b, 'little')
#Split n into 4 10 bit integers
yield n & 0x3ff
n >>= 10
yield n & 0x3ff
n >>= 10
yield n & 0x3ff
n >>= 10
yield n
这个应该提供更多的速度......
答案 1 :(得分:1)
由于在Python中没有通过x-bit读取文件x-bit的直接方法,我们必须逐字节地读取它。根据MisterMiyagi和PM 2Ring的建议,我修改了我的代码,用5个字节的块(即40位)读取文件,然后将得到的字符串分成4个10位数,而不是单独循环这些位。它的速度是我之前代码的两倍。
file = open("my_file", "rb")
pixels = []
exit_loop = False
try:
while not exit_loop:
# Read 5 consecutive bytes into fiveBytesString
fiveBytesString = ""
for i in range(5):
byte = file.read(1)
if not byte:
exit_loop = True
break
byteString = format(ord(byte), '08b')
fiveBytesString += byteString[::-1]
# Split fiveBytesString into 4 10-bit numbers, and add them to pixels
pixels.extend([int(fiveBytesString[i:i+10][::-1], 2) for i in range(0, 40, 10) if len(fiveBytesString[i:i+10]) > 0])
finally:
file.close()
答案 2 :(得分:0)
添加了一个适用于Numpy的解决方案,适用于解压缩大型10位打包字节缓冲区,例如您可能会从AVT和FLIR摄像机收到的缓冲区。
这是@cyrilgaudefroy's answer的10位版本,用于类似的问题;在那里,您还可以找到Numba替代产品,它可以进一步提高速度。
import numpy as np
def read_uint10(byte_buf):
data = np.frombuffer(byte_buf, dtype=np.uint8)
# 5 bytes contain 4 10-bit pixels (5x8 == 4x10)
b1, b2, b3, b4, b5 = np.reshape(data, (data.shape[0]//5, 5)).astype(np.uint16).T
o1 = (b1 << 2) + (b2 >> 6)
o2 = ((b2 % 64) << 4) + (b3 >> 4)
o3 = ((b3 % 16) << 6) + (b4 >> 2)
o4 = ((b4 % 4) << 8) + b5
unpacked = np.reshape(np.concatenate((o1[:, None], o2[:, None], o3[:, None], o4[:, None]), axis=1), 4*o1.shape[0])
return unpacked
如果返回缓冲区而不是Numpy数组,则可以忽略Reshape:
unpacked = np.concatenate((o1[:, None], o2[:, None], o3[:, None], o4[:, None]), axis=1).tobytes()
或者如果知道图像尺寸,则可以直接重塑图像,例如:
unpacked = np.reshape(np.concatenate((o1[:, None], o2[:, None], o3[:, None], o4[:, None]), axis=1), (1024, 1024))
如果模运算符的使用令人困惑,请尝试以下方法:
np.unpackbits(np.array([255%64], dtype=np.uint8))
编辑:事实证明,Allied Vision Mako-U摄像机采用的顺序与我最初在上面建议的顺序不同:
o1 = ((b2 % 4) << 8) + b1
o2 = ((b3 % 16) << 6) + (b2 >> 2)
o3 = ((b4 % 64) << 4) + (b3 >> 4)
o4 = (b5 << 2) + (b4 >> 6)
因此,如果最初显示出的图像对于您的特定设置来说看起来很奇怪,那么您可能必须测试不同的顺序。