画像の主要な色を検出するための高速なアルゴリズムを知っている人はいますか?
現在、k-means を使用して Python の PIL と一緒に色を見つけていますが、非常に遅いです。1 つの 200x200 画像の処理には 10 秒かかります。私は数十万の画像を持っています。
画像の主要な色を検出するための高速なアルゴリズムを知っている人はいますか?
現在、k-means を使用して Python の PIL と一緒に色を見つけていますが、非常に遅いです。1 つの 200x200 画像の処理には 10 秒かかります。私は数十万の画像を持っています。
迅速な方法の 1 つは、単純に色空間をビンに分割してから、ヒストグラムを作成することです。ピクセルごとに少数の決定のみが必要であり、画像に対して 1 回のパス (および最大値を見つけるためにヒストグラムに対して 1 回のパス) しか必要ないため、高速です。
更新:これは、私が何を意味するかを説明するのに役立つ大まかな図です。
X 軸は、個別のビンに分割された色です。y 軸は、各ビンの値を示します。これは、そのビンの色範囲に一致するピクセルの数です。この画像には 2 つの主要な色があり、2 つのピークで示されています。
少し手を加えれば、このコード(すでに見たことがあると思います!) を 1 秒未満まで高速化できます。
kmeans(min_diff=...)
値を約 10 に増やすと、非常に似た結果が得られますが、900 ミリ秒で実行されmin_diff=1
ます(.
サムネイルのサイズを 100x100 にさらに小さくしても、結果にはあまり影響がないようで、実行時間は約 250 ミリ秒になります。
min_diff
これは、値をパラメータ化するだけで、結果/タイミングを含むHTMLファイルを生成するためのひどいコードが含まれている、コードのわずかに調整されたバージョンです。
from collections import namedtuple
from math import sqrt
import random
try:
import Image
except ImportError:
from PIL import Image
Point = namedtuple('Point', ('coords', 'n', 'ct'))
Cluster = namedtuple('Cluster', ('points', 'center', 'n'))
def get_points(img):
points = []
w, h = img.size
for count, color in img.getcolors(w * h):
points.append(Point(color, 3, count))
return points
rtoh = lambda rgb: '#%s' % ''.join(('%02x' % p for p in rgb))
def colorz(filename, n=3, mindiff=1):
img = Image.open(filename)
img.thumbnail((200, 200))
w, h = img.size
points = get_points(img)
clusters = kmeans(points, n, mindiff)
rgbs = [map(int, c.center.coords) for c in clusters]
return map(rtoh, rgbs)
def euclidean(p1, p2):
return sqrt(sum([
(p1.coords[i] - p2.coords[i]) ** 2 for i in range(p1.n)
]))
def calculate_center(points, n):
vals = [0.0 for i in range(n)]
plen = 0
for p in points:
plen += p.ct
for i in range(n):
vals[i] += (p.coords[i] * p.ct)
return Point([(v / plen) for v in vals], n, 1)
def kmeans(points, k, min_diff):
clusters = [Cluster([p], p, p.n) for p in random.sample(points, k)]
while 1:
plists = [[] for i in range(k)]
for p in points:
smallest_distance = float('Inf')
for i in range(k):
distance = euclidean(p, clusters[i].center)
if distance < smallest_distance:
smallest_distance = distance
idx = i
plists[idx].append(p)
diff = 0
for i in range(k):
old = clusters[i]
center = calculate_center(plists[i], old.n)
new = Cluster(plists[i], center, old.n)
clusters[i] = new
diff = max(diff, euclidean(old.center, new.center))
if diff < min_diff:
break
return clusters
if __name__ == '__main__':
import sys
import time
for x in range(1, 11):
sys.stderr.write("mindiff %s\n" % (x))
start = time.time()
fname = "akira_940x700.png"
col = colorz(fname, 3, x)
print "<h1>%s</h1>" % x
print "<img src='%s'>" % (fname)
print "<br>"
for a in col:
print "<div style='background-color: %s; width:20px; height:20px'> </div>" % (a)
print "<br>Took %.02fms<br> % ((time.time()-start)*1000)
K-means は、主要な色の数が事前にわかっているため、このタスクに適しています。K-means を最適化する必要があります。画像サイズを縮小できると思います。100x100 ピクセル程度に縮小するだけです。アルゴリズムが許容できる速度で動作するサイズを見つけます。もう 1 つのオプションは、k-means クラスタリングの前に次元削減を使用することです。
そして、高速な k-means の実装を見つけようとします。そのようなことを Python で書くことは、Python の誤用です。こんな使い方はいけません。