【问题标题】:Fast algorithm to detect main colors in an image?检测图像中主要颜色的快速算法?
【发布时间】:2012-10-25 00:50:51
【问题描述】:

有人知道检测图像中主要颜色的快速算法吗?

我目前正在使用 k-means 和 Python 的 PIL 一起查找颜色,但速度很慢。处理一张 200x200 图像需要 10 秒。我有几十万张图片。

【问题讨论】:

  • 如果你真的需要速度,随机抽样可能是一种选择
  • 我认为 k-means 是一个不错的选择,因为您事先知道集群的数量。也许您需要优化您的实现以获得更好的性能,或者用 C 或 C++ 重写它。
  • 可以在我的博客文章中找到基于划分的集群的非常快速且开源的 C++ 实现:modejong.com/blog/post17_divquant_clustering

标签: python algorithm colors python-imaging-library


【解决方案1】:

稍加修改,this code(我怀疑你可能已经看到了!)可以加速到不到一秒

如果将kmeans(min_diff=...) 的值增加到大约 10,它会产生非常相似的结果,但运行时间为 900 毫秒(相比之下,min_diff=1 大约需要 5000-6000 毫秒)

将缩略图的大小进一步减小到 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)

【讨论】:

    【解决方案2】:

    一种快速的方法是简单地将颜色空间划分为 bin,然后构建直方图。它很快,因为您只需要对每个像素进行少量决策,并且只需要遍历图像一次(并且遍历直方图以找到最大值)。

    更新:这里有一个粗略的图表来帮助解释我的意思。

    在 x 轴上,颜色被划分为离散的 bin。 y 轴显示每个 bin 的值,即与该 bin 的颜色范围匹配的像素数。此图像中有两种主要颜色,由两个峰显示。

    【讨论】:

    • 最简单的方法是取直方图中的前五个 bin!您可能会发现跨越多个箱的脂肪峰 - 在这种情况下,您将希望找到 局部最大值 而不是绝对最大值(即,如果您想象带有“山丘”的直方图,其中最常见的颜色是,找到山顶,而不是可能都在一个最大的山上的前五个点)。您可能会发现先平滑直方图很有帮助。
    • 感谢@BrianL 的图表。现在很清楚了。唯一的问题是我不知道色调是什么。我将尝试找到有关 Hue 的更多信息。我可以从 RGB 中轻松找到 Hue 吗?
    • @bodacydo colorsys.rgb_to_hsv 这样做 - h 是色调
    【解决方案3】:

    K-means 是此任务的不错选择,因为您事先知道主要颜色的数量。您需要优化 K-means。我认为您可以减小图像大小,只需将其缩小到 100x100 像素左右。找到你的算法以可接受的速度工作的大小。另一种选择是在 k-means 聚类之前使用降维。

    并尝试找到快速的 k-means 实现。用python写这样的东西是对python的误用。不应该这样使用。

    【讨论】:

    • 谢谢@Lazin。我会尝试将图像转换为 100x100,我认为这应该会减少 4 的运行时间。也许 50x50 也可以。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-16
    • 2011-07-26
    • 1970-01-01
    相关资源
    最近更新 更多