在Numpy中找出前10(n)个RGB颜色

4

我有一个函数需要几秒钟的时间。这个函数应该返回给定图像中前n个色彩值。返回结果必须经过排序,以便我可以使用第一、第二、第三个最常出现的颜色的rgb值进行工作。

首先,我有一个PIL.Image对象,它循环遍历x、y坐标并在defaultdict中进行计数。我已经用Numpy数组替换了我的项目中的PIL对象,这给了我很大的提升,但我不知道如何在这种情况下替换defaultdict。

我的当前解决方案:

import numpy as np
from scipy import misc  # for example Image
from collections import defaultdict

def count_colors(img, n):
    img = img.reshape(-1, img.shape[-1])
    color = defaultdict(int)

    for pixel in img:
        rgb = (pixel[0], pixel[1], pixel[2])
        color[rgb] += 1

    sorted_color = sorted(color.items(), key=lambda k_v: k_v[1], reverse=True)
    sorted_color = sorted_color[:n]

    return sorted_color

img = misc.face()  # example Numpy Image array
top_colors = count_colors(img, n=5)

display(top_colors)

当前输出:

[((9, 9, 9), 1062),
 ((10, 10, 10), 700),
 ((8, 8, 8), 668),
 ((9, 7, 8), 586),
 ((9, 7, 10), 579)]

有没有Numpy的方法可以解决这个问题?

1个回答

3

方法一

我们可以使用np.unique(.., axis=0, return_counts=True)来获取每种唯一颜色的计数,然后使用np.argpartition在它们中间选择前N个颜色,以获得一个紧凑的向量化解决方案 -

def topN_colors(img, N):
    unqc,C = np.unique(img.reshape(-1,img.shape[-1]), axis=0, return_counts=True)
    topNidx = np.argpartition(C,-N)[-N:]
    return unqc[topNidx], C[topNidx]

方法 #2

另一种基于24位整数2D缩减的方案,旨在提供更加高效的解决方案 -

# https://stackoverflow.com/a/57236217/ @tstanisl
def scalarize(x):
    # compute x[...,2]*65536+x[...,1]*256+x[...,0] in efficient way
    y = x[...,2].astype('u4')
    y <<= 8
    y +=x[...,1]
    y <<= 8
    y += x[...,0]
    return y

def topN_colors_v2(img, N):
    img2D = scalarize(img)
    unq,idx,C = np.unique(img2D, return_index=True, return_counts=True)
    topNidx = np.argpartition(C,-N)[-N:]
    return img.reshape(-1,img.shape[-1])[idx[topNidx]], C[topNidx]

请注意,argpartition不保留顺序。为了保持顺序,请使用带有它的range()更多信息。因此,在np.argpartition中,用range(-N,0)替换-N以按升序获取颜色及其计数。对于降序,只需翻转最终输出即可。

使用示例进行验证

# Sample setup
np.random.seed(0)
# some random set colors
colors = np.array([[2,5,6],[1,2,3],[6,7,8],[5,3,1],[7,4,2]])

# Random image with colors chosen off colors
idx = np.random.randint(0,len(colors),(50,40))
img = colors[idx]
img = img.astype(np.uint8)

# Given that we know the unique colors as `colors` and the indices
# use to get the image `img, let's "manually" compute the 
# top N=2 colors and their counts
In [45]: count = np.bincount(idx.ravel())

In [46]: colors[count.argsort()[-2:]]
Out[46]: 
array([[1, 2, 3],
       [5, 3, 1]], dtype=uint8)

In [47]: count[count.argsort()[-2:]]
Out[47]: array([393, 446])

# Verify our solution
In [48]: topN_colors(img, N=2)
Out[48]: 
(array([[1, 2, 3],
        [5, 3, 1]], dtype=uint8),
 array([393, 446]))

是的,它能正常工作,你的第二种方法真的很快。我需要它按降序排列。你的范围(-N,0)是关键所在。我修改了返回结果:return img.reshape(-1,img.shape[-1])[idx[topNidx]][::-1], C[topNidx][::-1]。 - Incur
说实话,我不太理解scalarize函数。学无止境 :) - Incur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接