EnsekiTT Blog

EnsekiTTが書くブログです。

Pythonによるインメモリでのデータ圧縮の話 後編

つまりなにしたの?

Pythonではデータ圧縮とアーカイブが標準ライブラリにあり、そのうちデータ圧縮をやる。
バイナリデータをバイナリデータのまま圧縮して変数に格納する。
ensekitt.hatenablog.com
の続き

f:id:ensekitt:20180909003724j:plain

*1

圧縮アルゴリズム

基本的には、簡易関数のcompressとdecompressを使う方法にする
13. データ圧縮とアーカイブ — Python 3.6.5 ドキュメント

定性的なことは前編に書いた。

やってみる

やって見るデータ

個人的にちょっとやってみたいデータがあるので以下の4つのデータを圧縮してみる。

  • テキスト

日本語で1万字程度のテキストデータ
Wikipediaの秀逸な記事リストから長くてそこそこ語彙が多そうなので
シビュラの信託を選んだ。全体では2〜3万字あるので、先頭から1万字分だけ使った。

  • 音声

10分程度の音声(WAVE形式のステレオ音楽)
iMovieの音声データを適当につなげて書き出したものを使った。
無音区間がなく、そこそこ高音まであるものを選ぶ

  • 画像

3840x2160のフルカラー画像
いつもサムネイルに使わせてもらっているUnsplashからやや拡大して作った。
真っ黒とか真っ白なところの少ないもので周波数高めな画像を選んだ。
Jezael Melgoza (@lyonpixel) | Unsplash Photo Community

  • 大きいバイナリ

UbuntuのOSイメージ
www.ubuntu.com

やりかた

データの読み込み

ここではデータを取り込んで、bytes型のdataに格納する。
適当にクイックルックして、元のサイズを表示する。

  • テキスト
with open("text.txt", 'r') as f:
    data = f.read().encode('utf-8')

print("冒頭", data[:100].decode('utf-8'))    

print("raw size: ", len(data))
  • 音声
import wave
import numpy as np
with wave.open("audio.wav", 'r') as f:
    data = f.readframes(f.getnframes())
npdata = np.frombuffer(data,'int16')
print("raw size: ", len(data))

import matplotlib.pyplot as plt
plt.plot(npdata[10000:50000:2])
plt.plot(npdata[10001:50001:2])
plt.show()
  • 画像
import cv2
import matplotlib.pyplot as plt
data = cv2.imread("img.png")
plt.imshow(data)
  • 大きいイメージ
with open("img.iso", 'rb') as f:
    data = f.read()
print("raw size: ", len(data))
圧縮を行うコード

3種類の圧縮を行いつつ、各圧縮方式の圧縮率と圧縮率を計算してみた。
compressの引数に圧縮のレベルがある場合はそのレベルを1ずつ変えてどのような結果になるか試してみた。

import gzip
levels = [i for i in range(10)]
print("gzip")
for l in levels:
    %timeit c_data = gzip.compress(data, l)
    c_data = gzip.compress(data, l)
    print("gzip level: ", l, ", size: ", len(c_data), ", rate: ", len(c_data)/len(data))
    
import bz2
levels = [i for i in range(1,10)]
print("\nbz2")
for l in levels:
    %timeit c_data = bz2.compress(data, l) 
    c_data = bz2.compress(data, l) 
    print("bz2 level: ", l, ", size: ", len(c_data), ", rate: ", len(c_data)/len(data))
    
import lzma
print("\nlzma")
%timeit c_data = lzma.compress(data)
c_data = lzma.compress(data)
print("lzma level: ", l, ",size: ", len(c_data), ", rate: ", len(c_data)/len(data))
実行結果

テキストで出てくるものを整形し直している

  • テキストの場合

f:id:ensekitt:20180912014345p:plain
かなり高い圧縮率がどの形式においても発揮された

  • 音声の場合

f:id:ensekitt:20180912014426p:plain

  • 画像の場合

f:id:ensekitt:20180912014442p:plain
Bz2があまりうまく言っていない様子

  • 大きいイメージ

f:id:ensekitt:20180912014616p:plain
lzma圧倒的に時間がかかっている

おわりに

gzipが伝送コストがよっぽど高い場合以外は使い勝手が良さそう。
今更だけど、解答の速度もはかってみればよかった。
今度気が向いたらやってみる。

クリエイティブ・コモンズ・ライセンス
この 作品 は クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下に提供されています。