2016-12-18 12 views
3

11ビット整数のパックされたストリームを表すbytesオブジェクトまたはbytearrayオブジェクトがあります。 (編集:ストリームはパディングなしの11ビットビッグエンディアン整数です)11ビット整数を含むbytearrayを16ビット整数の配列にキャストする

これを16ビット整数のストリームにコピーする方法はありますか?または他の整数型?

私はctypesがビットフィールドをサポートしていることを知っていますが、これが私にはまったく役立つかどうかはわかりません。

他の目的のために既にこのようなビットバイディングを行っている標準ライブラリの一部を "乱用"できますか?

私はcythonに頼らなければならない場合は、可変ビット長に対応できる良い実装がありますか?私。 11ビット入力だけでなく、12,13などの入力も可能です。


編集:PM2リングの回答に基づいてピュア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 

答えて

2

あり、サードパーティのライブラリでこれを行うには、より効率的な方法であるが、ここでそれを行うための一つの方法だこと標準のPythonを使用します。

unpackジェネレータは、そのdata引数をチャンク単位で反復処理します。dataは、バイトを生成する任意の反復可能性があります。 11ビットのデータをアンパックするには、11バイトのチャンクを読み込み、それらのバイトを1つの整数に結合し、その整数を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))) 

出力databitlenバイトの倍数を含まない場合、それは無視される部分チャンクに終了すること

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

注意。

関連する問題