2016-12-18 3 views
3

Ich habe ein Byte-Objekt oder Bytearray-Objekt, das einen gepackten Strom von 11-Bit-Ganzzahlen darstellt. (Edit: Stream ist 11-Bit Big-Endian Ganzzahlen ohne Auffüllen.)Ein Bytearray mit 11-Bit-Ganzzahlen in ein Array von 16-Bit-Ganzzahlen umwandeln

Gibt es eine einigermaßen effiziente Möglichkeit, dies zu einem Strom von 16-Bit-Ganzzahlen zu kopieren? Oder irgendein anderer ganzzahliger Typ?

Ich weiß, dass Ctypes Bitfelder unterstützt, aber ich bin mir nicht sicher, ob mir das hier überhaupt hilft.

Kann ich vielleicht einen Teil der Standard-Bibliothek "missbrauchen", die schon für andere Zwecke so etwas falsch macht?

Wenn ich auf Cython zurückgreifen muss, gibt es eine gute Implementierung, die mit variablen Bitlängen umgehen kann? I.e. nicht nur für 11 bit eingang sondern auch 12, 13, etc?


Edit: reine Python-Lösung basierend auf PM2 Ring Antwort

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 

Antwort

2

Es kann ein effizienter Weg, dies mit einer 3rd-Party-Bibliothek zu tun, aber hier ist eine Möglichkeit, es zu tun mit Standard Python.

Der unpack Generator iteriert über sein data Argument in Chunks, data kann beliebig iterierbar sein, was Bytes ergibt. Um 11-Bit-Daten zu entpacken, lesen wir Blöcke von 11 Bytes, kombinieren diese Bytes zu einer einzigen ganzen Zahl und schneiden dann diese ganze Zahl in 8 Teile, so dass jedes Stück die Daten von den entsprechenden 11 Quell-Bits enthält.

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

Ausgang

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] 

anzumerken, dass, wenn es in einem data partielle Abschnitt kein Vielfaches von Bytes enthalten bitlen dann enden wird, die ignoriert werden sollen.

Verwandte Themen