テクノなまこ

科学の力

PILとmatplotlibで画像の色を抽出しグラフ化する

pythonで画像から色を抽出したい」と検索すると
Image Color Extraction with Python in 4 Steps | by Boriharn K | Towards Data Science
のような記事が出てくるが冗長である。処理ごとにコードを書かれるとコピペの手間もかかるしもったいぶらずにコードを全部載せてほしいし色を抜き出すところだけ見せてほしい。なのでこの記事にもっとずっと短いものを載せた。


上の記事で具体的に色を抽出する箇所は一行だけであり、それは、extcolors

extcolors.extract_from_image(img, tolerance = 10, limit = 5)

のような関数で実現できる。imgはPILの画像、toleranceは色をまとめる強度(0から100をとり、100で一色になる)、limitは色数の上限である。パスから直接読み込む場合は

extcolors.extract_from_path(img_url, tolerance = 10, limit = 5)

を使う。
これらの関数は色のタプルを含むちょっと入れ子になっているタプルを返す。

([((181, 198, 218), 18953), ((123, 152, 194), 18193), ((76, 83, 89), 8629), ((36, 46, 108), 5575), ((117, 118, 121), 4289)], 65536)

((赤, 青, 緑), ピクセル数)という構造が色の数だけ続いていて、最後に総ピクセル数がある。このタプルから割合を出すにはちょっと計算がいる。
このタプルを扱いやすい配列にしてグラフにするコード例↓
1.pngから5つの代表的な色を抜き出してグラフにしたいとする

import PIL
import matplotlib.pyplot as plt
import extcolors

filename = "1.png"
n = 5

img = PIL.Image.open(filename).resize((256,256)) #軽量化のため、縮小
colors, pixelCount = extcolors.extract_from_image(img, tolerance = 12, limit = n)
colorCodes = ['#{:02x}{:02x}{:02x}'.format(*rgb[0]) for rgb in colors]
colorRates = [rgb[1] for rgb in colors]
plt.pie(colorRates,labels=colorCodes,colors=colorCodes)
plt.savefig(filename+"_Color.png")
plt.show()

pillowは優秀なので、1.jpgとか1.jfifとか他フォーマットでも処理できる

出力

色分布を三次元散布図にする
from matplotlib import pyplot as plt
import cv2 as cv2
import numpy as np

img = cv2.imread("1.png")
img = cv2.resize(img,(64,64))

print(img)
r = img[:,:,2]
g = img[:,:,1]
b = img[:,:,0]

rgb = np.dstack((r, g, b))
rgb_flat = rgb.reshape((rgb.shape[0]*rgb.shape[1], 3))
def rgb_to_hex(rgb):
    return '#{:02x}{:02x}{:02x}'.format(*rgb)
cCodes = np.apply_along_axis(rgb_to_hex, 1, rgb_flat)

# print(cCodes)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
ax.scatter(b, g, r, c=cCodes,alpha=1)
plt.show()

出力