2012-10-25 78 views
7

有誰知道一個快速的算法來檢測圖像中的主要顏色?快速算法檢測圖像中的主要顏色?

我目前使用k-means與Python的PIL一起查找顏色,但速度很慢。一張200x200的圖像需要10秒才能處理。我有幾十萬張圖片。

+1

隨機抽樣,如果你真的真的需要速度 – jozefg

+0

我覺得K-手段可能是一種選擇是相當因爲您事先知道羣集數。也許你需要優化你的實現以獲得更好的性能,或者用C或C++重寫它。 – Lazin

+0

基於劃分聚類的非常快速和開源的C++實現可以在我的博客文章中找到:http://www.modejong.com/blog/post17_divquant_clustering – MoDJ

回答

9

一種快速的方法是簡單地將色彩空間分成多個分箱,然後構造一個直方圖。它的速度很快,因爲每個像素只需要少量的決策,而且只需要在圖像上進行一次掃描(並通過直方圖查找最大值)。

更新:這裏有一個粗略的圖表來幫助解釋我的意思。

在x軸上是分成不連續箱的顏色。 y軸顯示每個bin的值,它是與該bin的顏色範圍匹配的像素數。這個圖像中有兩個主要顏色,由兩個峯值顯示。

Color Histogram

+0

如果我想要5種頂級顏色,該怎麼辦? – bodacydo

+4

最簡單的方法是取直方圖中的前五個倉!你可能會發現坐在幾個箱子上的胖峯 - 在這種情況下,你會希望找到_local maxima_而不是絕對最大值(即,如果你想象出帶有「山丘」的頻率最高的顏色的直方圖,找到山頂,而不是可能全部位於最大山丘上的前五點)。您可能會發現首先平滑直方圖很有幫助。 –

+0

感謝@BrianL圖。現在非常清楚。唯一的問題是我不知道Hue是什麼。我會盡力找到更多關於Hue的信息。我可以從RGB輕鬆找到Hue嗎? – bodacydo

0

K-手段是完成這個任務的好選擇,因爲你知道的主色調數事前。你需要優化K-means。我認爲你可以減少你的圖像尺寸,只需將它縮小到100x100像素左右即可。查找你的算法在可接受的速度下工作的大小。另一種選擇是在k均值聚類之前使用降維。

並試圖找到快速k-means的實現。用python編寫這樣的東西是對python的濫用。不應該像這樣使用它。

+0

謝謝@Lazin。我會嘗試將圖像轉換爲100x100,這應該將運行時間減少4我認爲。也許50x50也可以工作。 – bodacydo

0

有了一點修修補補,this code(我懷疑你可能已經看到了!)可以略低於第二

如果增加kmeans(min_diff=...)值約10加速到,它產生的結果非常相似,但在900毫秒運行(相比與min_diff=1約5000-6000ms)

進一步降低縮略圖爲100x100的大小似乎並沒有太多或者影響結果,並採取了運行時間約250ms的

這是一個稍微調整的鱈魚版本E,這只是parameterises的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)