阅读 7

简单获取图片的主色

问题

我们想知道一张图片的主要颜色是什么?一般可以用RGB颜色来表示。

解决方案

一个简单的思路是对图片的所有像素点的不同颜色值进行统计,再找到像素点最多的那种颜色就可以了。这里我们以RGB颜色空间为例来处理。PIL和opencv等图像库都可以将图片文件加载为关于颜色的3维张量,以下以PIL为例。

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
复制代码
def get_rgb_tensor(path):
    img = Image.open(path)
    img = img.convert("RGB") # 通过转换确保图片为RGB格式
    img_tensor = np.asarray(img)
    return img_tensor

def show_img_tensor(img_tensor):
    plt.axis('off')
    plt.imshow(img_tensor)
复制代码
banana_rgb = get_rgb_tensor("assets/banana.jpg")
show_img_tensor(banana_rgb)
复制代码

banana

以下主要通过np.unique 完成像素点统计, 我们只看像素最多的前k种颜色

def pixels_simple_main_color(pixels, k):
    colors, counts = np.unique(pixels, return_counts=True, axis=0)
    max_indexes = np.argpartition(counts, -k)[-k:] # 查找最大的k个元素位置
    main_colors = colors[max_indexes]
    main_rates = counts[max_indexes] / len(pixels)
    return main_colors, main_rates

def simple_main_color(rgb_tensor, k=5):
    assert len(rgb_tensor.shape) == 3 
    assert rgb_tensor.shape[2] == 3
    pixels = rgb_tensor.reshape((-1, 3))
    return pixels_simple_main_color(pixels, k)

def show_colors(main_colors, main_rates):
    for color, rate in zip(main_colors, main_rates):
        print(f"color rgb = {color}, rate = {rate*100:.2f}%")
        color_tensor = np.zeros((20, 100, 3), dtype=np.uint8)
        color_tensor[:,:] = color
        plt.axis('off')
        plt.imshow(color_tensor)
        plt.show()
复制代码
show_colors(*simple_main_color(banana_rgb))
复制代码

计算结果均为绿色

color rgb = [ 82 128 53], rate = 0.15%

color rgb = [ 78 124 49], rate = 0.15%

color rgb = [ 79 125 50], rate = 0.16%

color rgb = [ 81 127 52], rate = 0.18%

color rgb = [ 80 126 51], rate = 0.24%

讨论

我们再尝试一张图片

tiger_rgb = get_rgb_tensor("assets/tiger.jpg")
show_img_tensor(tiger_rgb)
复制代码

tiger

show_colors(*simple_main_color(tiger_rgb))
复制代码

计算结果均为黑色

color rgb = [0 1 0], rate = 0.22%

color rgb = [0 0 2], rate = 0.23%

color rgb = [1 1 1], rate = 0.51%

color rgb = [1 0 0], rate = 0.23%

color rgb = [0 0 0], rate = 1.38%

和我们的主观感受不太一样,背景的黑色统治了结果,而老虎的棕色则看不出来。

这暴露了这个简单算法的最大问题:颜色往往是渐变的,不会固定到一个RGB值上,而我们用的是最简单的统计方法,需要RGB值完全一致才会合并统计,我们看到前5种颜色的占比其实很低。

这提示我们可能需要把颜色近似的像素点聚类到一起。具体的算法改天再谈。

在某些需求场景下,本期的简单算法也能满足需求,而且它相比聚类算法要快得多。