8

パケットでできているバイナリストリームを圧縮しています

パケットは、256個の32ビット整数(サンプル)で構成されます。重要なのは、ほとんどの整数が前の整数から数ビットしか変更されないことです(通常、ストリーム内の前のサンプルから最大で0〜4ビットが変更されます)。

次に例を示します。

3322 2222 2222 1111 1111 1110 0000 0000    BIT POSITIONS
1098 7654 3210 9817 6543 2109 8765 4321
--------------------------------------------------------
1100 1001 1110 0010 0001 0101 0110 1101    Sample 1  
               *                   * 
1100 1001 1110 1010 0001 0101 0110 0101    Sample 2     changes: bit 19, 4

1100 1001 1110 1010 0001 0101 0110 0101    Sample 3     changes: none
     *            *            *   
1100 0001 1110 1011 0001 0101 0010 0101    Sample 4     changes: bit 27, 17, 7
...

私の現在のlossles-compressionスキームは、ニブルに基づいています。基本的に、前のサンプルから変更されたニブルをエンコードする制御バイトを使用しています(シングルビットを使用)。変更がある場合は、変更されたニブルを圧縮ストリームに含めます。それ以外の場合は、解凍時に前のサンプルから再構築されます。

これが私が提供したサンプルストリームがどのように圧縮されるかです:

Control Byte: 11111111     // all nibbles change, since this is first sample
Data:         1100 1001 1110 0010 0001 0101 0110 1101 // data for all nibbles
Control Byte: 00010001     // only nibbles 3 and 7 have changes
Data:         1010 0101    // data for nibbles 3 and 7
Control Byte: 00000000     // no nibbles are changing
Data:                      // no data is required
Control Byte: 01010010     // nibbles 1, 3 and 6 have changes
Data:         0001 1011 0010   // nibbles 1, 3 and 6
...

このスキームを使用すると、256バイト(制御バイト)の固定オーバーヘッドがあり、平均の可変圧縮データ長は260バイト(サンプルごとに変化するニブル)になります。非圧縮パケットの長さが1024バイトであることを考慮すると、これにより、実質的に50%の平均圧縮率が得られます。

これは悪いことではありませんが、私の直感では、はるかに優れたアプローチが可能であると感じています。サンプルごとに変化するビットが非常に少ないという事実を利用した、より優れた圧縮戦略を知っている人はいますか?非可逆圧縮は、解凍後のビットエラー率が小さい(3%未満)限り、代替手段です。この特定のデータストリームの場合、ビット位置の数値の重みは関係ないため、上位ビットで発生するエラーは次のようになります。まったく心配ありません。

よろしくお願いします!

4

5 に答える 5

6

最初の整数を圧縮せずに送信し、他の 255 の整数については、この整数と前の整数の間で XOR を計算すると、ゼロ以外のビットが非常にまれなビットのストリームが得られます。このビット ストリームは、算術符号化でエンコードできます。

隣接値間の XOR を計算した後、ビットが互いに独立しているビット ストリームがある場合 (「0」または「1」の各ビットは同じ確率で、整数内のビット位置およびパケット内の整数位置に依存しません)。算術符号化により、最適な可逆圧縮率が保証されます。

于 2012-11-12T18:44:07.257 に答える
5

あなたの最善の策は、既存の技術 (例えば、Lempel-Ziv-Welch; flate) を使用するか、そのような方法の前に差分コーディングを使用することです (おそらくより良い方法です)。差分コーディングでは、すべてのバイト (最初のバイトを除く) をそのバイトと前のバイトの差分に置き換えます。これで、多数のゼロと、散在するいくつかの小さな値が得られるはずです。ハフマン コーディングまたは LZW のようなものは、ほぼゼロの文字列を完全に圧縮します。

于 2012-11-12T18:38:32.690 に答える
5

入力データに対して XOR を実行できます。わずかなビットしか変更されないため、これにより0、ほとんど1がその間にいくつかのビットで構成される結果が得られます。

1100 1001 1110 0010 0001 0101 0110 1101    Sample 1  
1100 1001 1110 1010 0001 0101 0110 0101    Sample 2     
1100 1001 1110 1010 0001 0101 0110 0101    Sample 3     
1100 0001 1110 1011 0001 0101 0010 0101    Sample 4     

開始値の後、これはシーケンスを生成します

0b0000 0000 0000 1000 0000 0000 0001 0000, 
0b0000 0000 0000 0000 0000 0000 0000 0000, 
0b0000 1000 0000 0010 0000 0000 1000 0000

さまざまな標準圧縮アルゴリズムを使用できるようになりました。8 バイト シーケンスのハフマン エンコーディング、LZW またはエントロピー エンコーディング。

4, 14, 51, 9, 9

実行の長さを 30 に制限し、「次の実行の長さに 31 を追加する」ことを意味するエスケープ記号 31 を選択すると、次のようになります。

4, 14, 31, 20, 9, 9

これは、シーケンス全体で 6*5 ビットになります。その上でハフマン符号化を行うことができます...

于 2012-11-12T19:04:59.980 に答える
1

私の考えはEvgenyKluevの考えと似ています。最初の整数は非圧縮で送信され、残りはそれ自体と前の整数のXORになります。

1100 1001 1110 0010 0001 0101 0110 1101    Sample 1  
               *                   * 
0000 0000 0000 1000 0000 0000 0000 1000    Sample 2

0000 0000 0000 0000 0000 0000 0000 0000    Sample 3
     *            *            *   
0000 1000 0000 0001 0000 0000 0100 0000    Sample 4

ここで、スパースデータをブロックに分割してここで算術符号化を行う代わりに、データをさらに変換します。実際、算術符号化は、データの頻度が等しくないことに基づいているためです。そしてこれを見て、あなたは思いますか

0000 0000 0000 1000 0000 0000 0000 1000

より頻繁に表示されます

0000 1000 0000 0001 0000 0000 0100 0000

またはその逆?

さて、これが私がデータをさらに変換する方法です。残りのデータを、連続するゼロの数を表す一連の数値にします。たとえば、データは次のようになります。

1100 1001 1110 0010 0001 0101 0110 1101    Sample 1  followed by decimals
12, 15, 39, 10, 9, 6

これで、これらの末尾の小数に対して算術符号化を実行できます。今回は頻度が理にかなっています!質問でほとんど変化がないと言ったので、連続するゼロの数が多いほど頻繁に表示されます。

編集:この答えはhirschhornsalzの答えとまったく同じです。彼はまた、ゼロの最大数に制限を設けてそれらを分割できると述べたことを除いて...

于 2012-11-12T19:10:58.633 に答える
1

あなたの例から、変化するいくつかのビットが常に同じであるとは限らないようです(たとえば、常に最低の4)。したがって、転置された配列のビットの単純なランレングス エンコーディングをお勧めします。数値/データの分布がなければ、長さは4ビットから始めることをお勧めしますが、入力例のいくつかで少し試すことができます。

疑似コード (圧縮用) は次のようになります。

 for bitpos = 0 to 31
     for datapos = 0 to 255 
         BitString.append(getbit(data[datapos], bitpos);
     endfor
 endfor

 result="";
 pos = 0;
 while (notEndOfString)
     # count 1s
     count = 0;
     while (pos < 32*256 AND count < 16 AND BitString[pos]==1)
         count++;
         pos++;
         endwhile
     result.append4BitNumber(count);
     # count 0s
     count = 0;
     while (pos < 32*256 AND count < 16 AND BitString[pos]==0)
         count++;
         pos++;
         endwhile
     result.append4BitNumber(count);
 endwhile

おそらく、後で Lempel-Ziv またはハフマン エンコーディングを適用することで圧縮率を高めることができますが、入力データの分布に関する情報がなければ、それ以上のことは言えません (これは一般的にこの問題に当てはまります - 入力データのより良い情報があれば、 、ある種の圧縮をそれに合わせることができます)。

EDIT:別の簡単なアプローチは、変化するビット位置のエンコードを作成することです。最初の32ビットワードから始めて、データワードごとに3ビットを保存して、どのくらいのビットが変化するかを定義します(つまり、0..7)、その後0..7 x 4 ビットを格納し、4 ビットはチャンニング ビットの位置をエンコードします。つまり、たとえば、32*256 ビット パケットに平均 2 ビットの変更が必要な場合、32+255*(3+8)=2837 => 元のサイズの約 35% が必要になります。

同じ数のビットが頻繁に変化する場合、これらの 4 ビット パターンの一部は非常に頻繁に表示されますが、他のパターンはまったく表示されません => これらの 4 ビット グループのハフマン コーディングは、最適に圧縮します (これらのパターンの確率がわかっている場合)。静的なハフマン木を作成することさえできるので、それを保存する必要はありません)。

于 2012-11-12T19:02:16.367 に答える