Python - 从二进制文件中读取10位整数

时间:2016-07-11 08:52:18

标签: python file-io binary

我有一个包含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) ...

这样的东西

3 个答案:

答案 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)

因此,如果最初显示出的图像对于您的特定设置来说看起来很奇怪,那么您可能必须测试不同的顺序。