8

画像の主要な色を検出するための高速なアルゴリズムを知っている人はいますか?

現在、k-means を使用して Python の PIL と一緒に色を見つけていますが、非常に遅いです。1 つの 200x200 画像の処理には 10 秒かかります。私は数十万の画像を持っています。

4

3 に答える 3

10

迅速な方法の 1 つは、単純に色空間をビンに分割してから、ヒストグラムを作成することです。ピクセルごとに少数の決定のみが必要であり、画像に対して 1 回のパス (および最大値を見つけるためにヒストグラムに対して 1 回のパス) しか必要ないため、高速です。

更新:これは、私が何を意味するかを説明するのに役立つ大まかな図です。

X 軸は、個別のビンに分割された色です。y 軸は、各ビンの値を示します。これは、そのビンの色範囲に一致するピクセルの数です。この画像には 2 つの主要な色があり、2 つのピークで示されています。

カラーヒストグラム

于 2012-10-25T04:46:52.380 に答える
1

少し手を加えれば、このコード(すでに見たことがあると思います!) を 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'>&nbsp;</div>" % (a)
        print "<br>Took %.02fms<br> % ((time.time()-start)*1000)
于 2012-10-25T12:31:48.130 に答える
0

K-means は、主要な色の数が事前にわかっているため、このタスクに適しています。K-means を最適化する必要があります。画像サイズを縮小できると思います。100x100 ピクセル程度に縮小するだけです。アルゴリズムが許容できる速度で動作するサイズを見つけます。もう 1 つのオプションは、k-means クラスタリングの前に次元削減を使用することです。

そして、高速な k-means の実装を見つけようとします。そのようなことを Python で書くことは、Python の誤用です。こんな使い方はいけません。

于 2012-10-25T06:32:22.043 に答える