シャープのポケコンで通常使用されているGPRINT形式の画像データですが、縦8ドットを2桁の16進数(2バイト)で表記しているので、メモリ効率が良いとは言えません。
そこで、このデータを圧縮する試みをしてみました。
《このソフト(OGE BASIC edition)の特長》
|
《注意》
ポケコン用画像圧縮・解凍ソフト OGE |
圧縮アルゴリズム
|
「G」〜「Z」を変換文字コード 0〜19
「ア」〜「ワ」を変換文字コード20〜63と仮定する
辞書データ(4バイト)を先頭から辞書データ0〜3とする
圧縮されるデータが辞書データのどれかと一致する場合「辞書データのコード×16+次の文字(10進数に直したもの)」となる変換文字コードの文字(0〜63)に置き換える
(例)
辞書データが「F235」の場合に画像データ「24」を圧縮する。「2」が辞書データ1に一致するので、「1×16+4=20」となり、変換文字コード20に相当する「ア」と置き換える。
《圧縮率》
理論上:50.0%〜87.5%(辞書4バイトを除いた場合)
圧縮率が最低になるのは0〜Fが1回ずつ出現し、かつ、「0123」が連続して出現(順番は問わない)した場合です。
(例)
「012345678ABCDEF」を圧縮した場合、「Hタ456789ABCDEF」となる(16バイトが14バイトに圧縮)
しかし、これは特殊なケースであり一般的な画像では圧縮率が80%以上になることはほとんどないでしょう。
【E500シリーズ用】
10 DIM P(15),Q(15) 20 L=LEN D$:FOR I=0TO 15:P(I)=0,Q(I)=I:NEXT :FOR I=1TO L:H=I:GOSUB *MID:P(M)=P(M)+1:NEXT 30 FOR J=14TO 0STEP -1:FOR I=J TO 0STEP -1:IF P(I)<P(I+1)LET P=P(I),P(I)=P(I+1),P(I+1)=P,Q=Q(I),Q(I)=Q(I+1),Q(I+1)=Q 40 NEXT :NEXT :J$="":FOR I=0TO 3:J$=J$+HEX$ Q(I):NEXT :CLS :PRINT J$:E$=J$ 50 FOR I=1TO L:H=I+1:GOSUB *MID:Y=M,H=I:GOSUB *MID:X=M 60 FOR J=0TO 3:IF I<L AND A$=MID$ (J$,J+1,1)LET Z=J*16+Y,A$=CHR$ (Z+71-(Z>19)*86),I=I+1ELSE NEXT 70 E$=E$+A$:PRINT A$;:NEXT I :END 80 *MID A$=MID$ (D$,H,1),M=VAL ("&"+A$):RETURN |
10 DIM P(15),Q(15),D$(1)*254 20 L=LEN D$(0):FOR I=0TO 15:P(I)=0,Q(I)=I:NEXT :FOR I=1TO L:H=I:GOSUB *MID:P(M)=P(M)+1:NEXT 30 FOR J=14TO 0STEP -1:FOR I=J TO 0STEP -1:IF P(I)<P(I+1)LET P=P(I),P(I)=P(I+1),P(I+1)=P,Q=Q(I),Q(I)=Q(I+1),Q(I+1)=Q 40 NEXT :NEXT :J$="":FOR I=0TO 3:J$=J$+CHR$ (Q(I)+48-(Q(I)>9)*7):NEXT :CLS :PRINT J$:D$(1)=J$ 50 FOR I=1TO L:H=I+1:GOSUB *MID:Y=M,H=I:GOSUB *MID:X=M 60 FOR J=0TO 3:IF I<L AND A$=MID$ (J$,J+1,1)LET Z=J*16+Y,A$=CHR$ (Z+71-(Z>19)*86),I=I+1:GOTO 80 70 NEXT 80 D$(1)=D$(1)+A$:PRINT A$;:NEXT I :END 90 *MID A$=MID$ (D$(0),H,1),M=ASC A$,M=M+(M>9)*8-48:RETURN |
変数D$に画像データを入れるとE$に圧縮されたデータが入ります。
なお、2ヵ所使用しているPRINT文は圧縮過程が分かるように表示しているだけなので不要なら入力の必要はありません。
【E500シリーズ用】
10 E$="",J$=LEFT$ (D$,4):FOR I=5TO LEN D$:A$=MID$ (D$,I,1),B=ASC A$:IF B>70LET C=B-55+(B>176)*86,A$=MID$ (J$,C/16,1)+HEX$ (C AND 15) 20 E$=E$+A$:NEXT |
10 DIM D$(1)*254 20 D$(1)="":J$=LEFT$ (D$(0),4):FOR I=5TO LEN D$(0),A$=MID$ (D$(0),I,1),B=ASC A$:IF B>70LET C=B-55+(B>176)*86,H=C AND 15,A$=MID$ (J$,C/16,1)+CHR$ (H+48-(H>9)*7) 30 D$(1)=D$(1)+A$:NEXT |
変数D$に圧縮されたデータを入れるとE$に元のデータが入ります。
このプログラムではGPRINTのデータを辞書にヒットするデータのみ2桁の文字を1桁の別の文字に置き換えることで圧縮していますが、00〜FFまで256種類ある並びを4文字分の辞書で賄う(4バイト辞書で前作の64バイト分の性能!)ということをしています。
しかし、調べているのが1桁ごとなので実際は0〜Fの16通りの中の4文字分となり、辞書にヒットする確率の単純計算(以下「推定ヒット率」と略)は25%となりそうです。
では、0〜Fの並びが完全にランダムで一様の確率で発生しているという仮定のものに推定ヒット率を考えていきます。
次のデータはこれから圧縮されるデータの一部分です。
1F3
ここで多くの皆さんが「F」がヒットする確率は25%であると考えるかもしれません。しかし、手前の「1」がヒットした場合は「F」と1まとめにされるので実質「F」がヒットしたのと同じことになります。
そうなると「F」はヒットするのは、25+25で50%となると考える人もいるかもしれませんが、それも違います。
というのも「1」がヒットしてしまったら「F」も無くなってしまうので、単純に加算が出来ないのです。
つまり、「『1』がヒットする確率+『1』がヒットしない、かつ、『F』がヒットする確率」が実質的に「F」がヒットする確率に等しいのです。
ということで「0.25+0.75*0.25=0.4375」となり「F」の推定ヒット率は約44%(厳密に言えばデータの最初と最後の文字にはこの計算は当てはまらないのですが、おおざっぱな「推定」ということなので細かい計算は省略します)となります。
この場合の推定圧縮率を計算してみると、約44%の確率で2バイトが1バイトになるので、約22%のデータ削減ができ圧縮率は約78%(辞書の4バイトを除いた場合)と推定されます。
ここまで書くとたいしたことはないと思いがちですが、あくまでこれは完全にランダムでデータの割合が完全に一様な場合です。
RND命令を使ってランダムなデータを作成した場合でも、全データに含まれる文字のうちの辞書に含まれる文字が入っている割合は30%程度(推定ヒット率51%→推定圧縮率74%)であり、人の手によって描かれた画像では50〜70%(推定ヒット率75%〜91%→推定圧縮率62%〜54%)となります。
ただし、人の手によって描かれた画像の場合は、データに偏りが見られることが多く、辞書に含まれる文字が一部に集中する可能性もあり、実際の圧縮率は推定圧縮率と比べ数%(概ね3〜5%)落ちてしまうことがあります。しかし、そういう場合は可変式辞書やランレングス法が有効なので、デラックス版(「拡張性について」参照)では逆に高圧縮できる可能性があります。
でも、一般的な画像ならば、このベーシック版でも60〜70%に圧縮可能なので大きな画像(32*32ドットを越えるもの)でなければ、圧縮率の心配はあまりないでしょう。
一般的に考えると圧縮率を高めるためには辞書の量を増やした方が効果的なのですが、なぜ4バイトなのかを書いておきます。
このプログラムでは2つの文字を1つの別の文字に置き換えることでデータ圧縮を実現していますが、元のデータに使われているのが0〜Fの16種類のため辞書を1バイト増やすごとに16文字分の文字コードが必要になります。キーボードから入力できる文字種には限りがあり(E500シリーズでは158文字程度)、さらに圧縮データに使用する文字種を増やせばデータの視認性の低下(このプログラムでは辞書のデータの中で頻度が高いものをアルファベットに充てることで、より視認性を高めている)や拡張性の低下(複数のアルゴリズムの組み合わせが困難)などを引き起こしてしまいます。
さらに、たとえほんの数バイトの辞書とはいえ小さい画像(概ね16*16ドット以下)では実質的な圧縮率の低下につながってしまいます。16*16ドットの画像では元のデータが64バイトのため辞書を1バイト増やすごとに実質圧縮率が約1.7%低下してしまいます。
逆に4バイトよりも少なくした場合を考えてみると、頻度が高いものを辞書データから外すことになるので、小さい画像を除き圧縮率の大きな低下(辞書データが減る量よりも圧縮できる量の低下の方が大きい)につながってしまいます。
これらのことを総合的に判断し、一般に良く使われるであろうサイズ(12*8〜24*24ドット)を想定して辞書のサイズを4バイトに固定としました。これ以上のサイズでもデラックス版を使えば4バイトの辞書でも十分に高圧縮が可能です。
このプログラムでは文字コードに空きがありそれを有効に使うことでさまざまな圧縮アルゴリズムを追加することができます。それらの追加機能をつけたものをここでは「デラックス版」と呼びます。
デラックス版では、ランレングス法や可変式辞書の追加を予定しています。
ベーシック版では幾ら同じデータが連続していても、理論上50%までしか圧縮できませんが、ランレングス法を用いることにより数倍〜数10倍に圧縮が可能となります。なお、ランレングス法についてここでは詳しくは述べません。
また、可変式辞書を使用によりデータ全体としては文字の頻度に偏りがなくても部分的に偏っている場合にかなりの圧縮が可能です。それは、一時的に辞書の内容を変えることによりヒット率の大幅な向上が期待できるからです。
ちなみにランレングス法と可変式辞書は併用が可能なのでさらなる圧縮が可能となります。
これらのことを追加したデラックス版の圧縮データはベーシック版のものと比べ視認性の低下が予想されますが、圧縮データはベーシック版のものをベースとしているので、全体的な視認性の低下が起こることはありません。また、使用された部分は確実にベーシック版よりもデータ圧縮がされているのでそれとトレードオフになっていると思えば問題ないでしょう(笑)。したがって、常にデラックス版を使うのではなくベーシック版と使い分ける(圧縮率を取るか、解凍プログラムの短さや圧縮データの分かりやすさを取るか)ということも考えた方がよいかもしれません。
なお、デラックス版で圧縮したデータはベーシック版で解凍することは出来ませんが、デラックス版では両方解凍が可能になります。
それから、今後期待される動画圧縮の基本圧縮アルゴリズムとしてもベーシック版は活用可能となるために将来性(?)も有望です。