LoginSignup
15
15

More than 5 years have passed since last update.

Python で画像の代表色 (?) を取る練習

Last updated at Posted at 2015-06-07

画像の代表色を調べたくなった。
しかも、自分なりに代表色の探し方を考えて、それを実装をしてみる練習をしたいとも思った。

大枠で次のような方式を考えて実装した。

  1. 画像のピクセルを k-means でクラスタに分ける。
  2. クラスタに属するピクセルの数でヒストグラムを作る。
  3. ヒストグラムの多い順で取り出して代表色とする。

k-means よりも優れた減色アルゴリズムは多数あり、そちらを使うのも手かもしれないが、
自分は日曜プログラミングで実装できる自信がなかったので、実装も簡単で scipy にも含まれてる k-means を使った。
ピクセルをクラスタに割り治すのも scipy の vq (Vector Quantization) をそのまま使うことにしたのでかなり捗った。

結果

wkhtmltoimage コマンドを使い、幾つかの Web ページの画像を取得して代表色を抜き出してみた。
抜き出しただけでは直感的にはわからないので、適当な長方形に代表色を同じ大きさで表示することにした。
以下、根拠はないが「いい感じ」と思った。

Qiita

qiita_image.png

Facebook

facebook_image.png

コカ・コーラ公式ブランドサイト

cocacola_image.png

スクリプト

以下が Python 3 で書いたスクリプト。
他に numpy, scipy, pillow を必要とする。

#!/usr/bin/env python
import argparse

import numpy

import PIL
import PIL.ImageDraw
import scipy
import scipy.cluster
import scipy.misc


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('source_image')
    parser.add_argument('summary_image')
    parser.add_argument('-n', type=int, default=4)
    args = parser.parse_args()
    img = PIL.Image.open(args.source_image)
    c = args.n + 1
    colors = top_n_colors(img, top_n=args.n, num_of_clusters=c)
    save_summary_image(args.summary_image, colors)


def pillow_image_to_simple_bitmap(pillow_image):
    small_img = pillow_image.resize((100, 100))
    bitmap = scipy.misc.fromimage(small_img)
    shape = bitmap.shape
    bitmap = bitmap.reshape(scipy.product(shape[:2]), shape[2])
    bitmap = bitmap.astype(numpy.float)
    return bitmap


def top_n_colors(pillow_image, top_n, num_of_clusters):
    clustering = scipy.cluster.vq.kmeans
    bitmap = pillow_image_to_simple_bitmap(pillow_image)
    clusters, _ = clustering(bitmap, num_of_clusters)
    quntized, _ = scipy.cluster.vq.vq(bitmap, clusters)
    histgrams, _ = scipy.histogram(quntized, len(clusters))
    order = numpy.argsort(histgrams)[::-1][:top_n]
    for idx in range(top_n):
        rgb = clusters.astype(int)[order[idx]].tolist()
        yield '#{:02x}{:02x}{:02x}'.format(*rgb)


def save_summary_image(path, color_codes, width=300, height=100):
    color_codes = tuple(color_codes)
    image = PIL.Image.new('RGB', (width, height))
    draw = PIL.ImageDraw.Draw(image)
    single_width = width / len(color_codes)
    for i, color_code in enumerate(color_codes):
        starting = (int(single_width * i), 0)
        ending = (int(single_width * (i + 1)), height)
        draw.rectangle([starting, ending], fill=color_code)
    image.save(path, format='png')


if __name__ == '__main__':
    main()

使い方

$ python ./image_top_n_color.py input.png output.png

-n オプションでクラスタの数を指定することができる。

15
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
15