Pythonで画像の減色をする

PythonのPillow(PIL)モジュールを使って、画像を減色してみました。

目次

  1. フルカラー画像を256色にする
  2. Pillowのconvertメソッドでモード変換する
  3. quantizeメソッドで減色する(median cut)
  4. quantizeメソッドで減色する(maximum coverage)
  5. quantizeメソッドで減色する(fast octree)
  6. quantizeメソッドで減色する(libimagequant)
  7. k平均法を100回かけてみる
  8. まとめ

フルカラー画像を256色にする

ウェブサイトに使う画像データなどは軽ければ軽いほど良いわけで、ビットマップよりJPEGやGIFやPNGが使われます。そこで、今回はフルカラー画像を256色に減色してみました。

サンプルの画像は3つです。

自前のカラーグラデーション画像

Pillowを使って作成した24 bitカラーの画像です。

カラーグラデーション画像

この画像を生成したときのコードはこんな感じです。

import numpy as np
from PIL import Image

line_data = np.arange(256)
hue = np.tile(line_data, (256,1))
sat = np.transpose(hue)
val = np.full_like(hue, 255)

bw = np.stack([np.full_like(hue, 255), np.full_like(hue,0), hue], 2)
mat1 = np.stack([hue, sat, val], 2)
mat2 = np.stack([hue, val, sat], 2)

mat = np.concatenate([mat1, mat2, bw], 1)

im = Image.fromarray(np.uint8(mat), 'HSV')
im_rgb = im.convert('RGB')
im_rgb.save('out.png')

写真

Pixabay の画像です。

写真

イラスト

プロ生ちゃん のイラストです。

イラスト

Pillowのconvertメソッドでモード変換する

Pillowの画像オブジェクトのモードを24 bitカラー(RGB)から8 bitカラー(P)に切り替えます。GIFやPNG-8で保存する前に行う処理です。

コードはこんな感じです。

from PIL import Image

im = Image.open('in.png')
c = im.getcolors(im.width * im.height)
print('Original colors :', len(c))

im_p = im.convert('P')
c = im_p.getcolors(im.width * im.height)
print('Modified colors :', len(c))
im_p.save('out.png')
オリジナル 減色後
119467色 174色
15KB 64KB
カラーグラデーション画像 convert
オリジナル 減色後
28923色 56色
36KB 82KB
写真 convert
オリジナル 減色後
31532色 74色
70KB 61KB
イラスト convert

JPEGってすごいですね。

quantizeメソッドで減色する(median cut)

Pillowのquantizeメソッドで減色します。いくつかアルゴリズムが選べるのですが、まずはmedian cutです。

減色アルゴリズムについては、 こちらのサイト が参考になります。

コードはこんな感じです。

from PIL import Image

im = Image.open('in.png')

im_q = im.quantize(colors=256, method=0, dither=1)
im_q.save('out.png')
オリジナル 減色後
119467色 228色
15KB 10KB
カラーグラデーション画像 median cut
オリジナル 減色後
28923色 256色
36KB 121KB
写真 median cut
オリジナル 減色後
31532色 256色
70KB 88KB
イラスト median cut

quantizeメソッドで減色する(maximum coverage)

次はmaximum coverageにしてみます。quantizeメソッドの引数が変わります。

from PIL import Image

im = Image.open('in.png')

im_q = im.quantize(colors=256, method=1, dither=1)
im_q.save('out.png')
オリジナル 減色後
119467色 256色
15KB 12KB
カラーグラデーション画像 max
オリジナル 減色後
28923色 256色
36KB 86KB
写真 max
オリジナル 減色後
31532色 256色
70KB 55KB
イラスト max

quantizeメソッドで減色する(fast octree)

続いて、fast octreeで減色します。

from PIL import Image

im = Image.open('in.png')

im_q = im.quantize(colors=256, method=2, dither=1)
im_q.save('out.png')
オリジナル 減色後
119467色 256色
15KB 7KB
カラーグラデーション画像 octree
オリジナル 減色後
28923色 249色
36KB 76KB
写真 octree
オリジナル 減色後
31532色 256色
70KB 60KB
イラスト octree

quantizeメソッドで減色する(libimagequant)

libimagequantという指定もできるようなのですが、私の環境では動きませんでした。

k平均法を100回かけてみる

アルゴリズムをmedian cutに戻して、kmeansオプションを100にしてみます。パレットの最適化がされるはずです。

from PIL import Image

im = Image.open('in.png')

im_q = im.quantize(colors=256, method=0, kmeans=100, dither=1)
im_q.save('out.png')
オリジナル 減色後
119467色 253色
15KB 11KB
カラーグラデーション画像 kmeans
オリジナル 減色後
28923色 256色
36KB 119KB
写真 kmeans
オリジナル 減色後
31532色 256色
70KB 86KB
イラスト kmeans

まとめ

カラーグラデーション画像

オリジナル convert median cut max coverage fast octree median cut + kmeans
119467色 174色 228色 256色 256色 253色
15KB 64KB 10KB 12KB 7KB 11KB

写真

オリジナル convert median cut max coverage fast octree median cut + kmeans
28923色 56色 256色 256色 249色 256色
36KB 82KB 121KB 86KB 76KB 119KB

イラスト

オリジナル convert median cut max coverage fast octree median cut + kmeans
31532色 74色 256色 256色 256色 256色
70KB 61KB 88KB 55KB 60KB 86KB

減色のアルゴリズムでできあがりの画像の感じが違うことがわかりましたが、元のファイルが小さくてファイルサイズの圧縮にはつながりませんでした。JPEGってすごいのね。

広告

Pythonで画像処理カテゴリの投稿