如何自动确定对比度最高的RGBA图像的背景颜色?

pas*_*cal 4 python graphics opencv image-processing computer-vision

背景

我有一堆 RGBA 图像。想象一下我想要显示的图标、徽标或类似图像。这些图像可以是任何颜色。另请注意,图像为 RGBA,因此它们具有第四个 Alpha 通道以实现透明度。

并发症

最初,我将所有图像显示在白色背景上。然而,有时整个图像或部分图像是白色的,因此这些区域对用户来说看起来是空的。

问题是图像不是全白或全黑的。可能有多种颜色和颜色渐变。而且,由于 RGBA 图像的工作方式,像素并不总是都是一种颜色并且都是不透明的。即使图像仅在中心显示纯黑色图标,黑色图标的边缘也可能存在过渡区域,其中像素具有一定的透明度或更偏灰色。

问题

假设,对于每个图像,我可以选择黑色、灰色或白色背景。如何通过算法确定三种背景颜色选项中哪一种最适合任何单个 RGBA 图像?

我所说的“最佳”是指图像的任何区域都不应错误地显示为空,并且图像和背景之间可以实现最高对比度。

例子

在下面的玩具示例中,我要显示的图标周围有一个白色圆圈。所以我不能使用白色作为背景色。我需要选择灰色或黑色。黑色是最好的,因为它可以最大限度地提高对比度。

玩具示例1

在第二个玩具示例中,事情稍微复杂一些,因为不同颜色的多个区域直接接触图像的透明区域。黑色背景似乎具有最高的对比度,但它不再是微不足道的。

玩具示例1

想法

  • 不透明区域的内部并不重要。背景颜色不会影响可见性。
  • 完全透明区域的内部部分并不重要。没有什么是看不见的。
  • 我可能需要在不透明和透明之间的这些过渡区域中找到那些像素。然后确定这些的颜色。

这看起来效率极低。由于大多数图标、徽标等都是针对明亮或黑暗背景设计的,因此在不透明和透明之间的过渡区域中仅采样几个像素是否就足够了?

我觉得我正在重新发明轮子,之前一定有人找到了解决这个问题的方法。

Stackoverflow 上的现有问题

  • 我发现有关确定给定背景颜色的最佳字体颜色的现有问题。然而我的问题是不同的。我看到的问题只是处理最大化两种简单 RGB 颜色之间的对比度。

附加示例3

请注意该徽标周围有一条细白线。这意味着最佳背景颜色可能应该是黑色。

在此输入图像描述

可以使用的示例

这里有十几个左右的 RGBA 图像可供使用。我不拥有版权——这只是为了说明问题。 谷歌云端硬盘链接

马克·塞切尔编辑

我已经添加了所有示例图像,从左到右如下:

  • 在棋盘上显示透明区域
  • 在黑色背景
  • 在灰色的背景
  • 在白色背景
  • 仅提取 alpha 通道

在此输入图像描述

nat*_*ncy 5

这个想法是计算图像的最“主导”颜色,然后选择与图像中存在的最强颜色最相反的相应背景颜色。因为我们只能选择白色、灰色或黑色;我们可以假设使对比度最大化的最佳颜色等于与最主要颜色相反的颜色。这是一种使用K 均值聚类来确定图像的主色的方法sklearn.cluster.KMeans()


我们可以选择的三种可能的RGB颜色代码是:

White: rgb(255, 255, 255)
Gray: rgb(128, 128, 128)
Black: rgb(0, 0, 0)
Run Code Online (Sandbox Code Playgroud)

我们可以通过找到主色的平均值来计算最佳颜色,并将其在颜色区域之间进行分类。由于颜色通道的范围为[0 - 255],我们可以简单地将其分为三个象限:

# Find best color
if dominant_color_average <= 85:
    print('White!')
elif dominant_color_average > 85 and dominant_color_average <= 170:
    print('Gray!')
elif dominant_color_average > 170:
    print('Black!')
Run Code Online (Sandbox Code Playgroud)

这里有些例子:

其中n_clusters=5,以下是最主要的颜色和百分比分布

[217 215 213] 2.66%
[158 156 154] 2.80%
[84 82 79] 3.02%
[19 16 14] 6.87%
[254 254 254] 84.66%
Run Code Online (Sandbox Code Playgroud)

每种颜色的可视化(在深色背景上,以便您可以看到白色)

结果

Dominant color: [254, 254, 254]
Dominant color average: 254
Black!
Run Code Online (Sandbox Code Playgroud)

第二张图片

其中n_clusters=5,以下是最主要的颜色和百分比分布

[ 27 172 221] 0.84%
[179 186 188] 1.99%
[239 241 241] 10.98%
[118 118 118] 21.18%
[254 254 254] 65.02%
Run Code Online (Sandbox Code Playgroud)

每种颜色的可视化(在深色背景上,以便您可以看到白色)

结果

Dominant color: [254, 254, 254]
Dominant color average: 254
Black!
Run Code Online (Sandbox Code Playgroud)

您的两个示例徽标都以白色为主,因此最好的背景应该是黑色。可能有一种更有趣的方法来计算最佳颜色,但我将其留给您。


徽标并非以白色为主的结果

在此输入图像描述

[249 247 249] 8.34%
[197  46 140] 21.02%
[173  74 213] 22.45%
[149 150 215] 22.68%
[205 103  84] 25.51%

Dominant color: [205, 103, 84]
Dominant color average: 130
Gray!
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

[220 246 246] 0.84%
[ 25 210 234] 11.26%
[  8  74 111] 22.17%
[  7 126 175] 23.17%
[ 2 28 45] 42.56%

Dominant color: [2, 28, 45]
Dominant color average: 25
White!
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

[120 180 218] 2.51%
[  2  89 148] 5.76%
[245 245 248] 8.51%
[237  29  59] 11.43%
[  0 123 196] 71.79%

Dominant color: [0, 123, 196]
Dominant color average: 106
Gray!
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

[ 17 124 143] 1.53%
[26 75 84] 1.74%
[  7 173 203] 1.84%
[  0 215 254] 22.30%
[33 34 34] 72.58%

Dominant color: [33, 34, 34]
Dominant color average: 33
White!
Run Code Online (Sandbox Code Playgroud)

代码

import cv2
import numpy as np
from sklearn.cluster import KMeans

def visualize_colors(cluster, centroids, exact=False):
    # Get the number of different clusters, create histogram, and normalize
    labels = np.arange(0, len(np.unique(cluster.labels_)) + 1)
    (hist, _) = np.histogram(cluster.labels_, bins = labels)
    hist = hist.astype("float")
    hist /= hist.sum()
    
    # Convert each RGB color code from float to int
    if not exact:
        centroids = centroids.astype("int")
    
    # Create frequency rect and iterate through each cluster's color and percentage
    rect = np.zeros((50, 300, 3), dtype=np.uint8)
    colors = sorted([(percent, color) for (percent, color) in zip(hist, centroids)])
    start = 0
    for (percent, color) in colors:
        print(color, "{:0.2f}%".format(percent * 100))
        end = start + (percent * 300)
        cv2.rectangle(rect, (int(start), 0), (int(end), 50), \
                      color.astype("uint8").tolist(), -1)
        start = end
    return colors, rect

# Load image and convert to a list of pixels
image = cv2.imread('1.png')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
reshape = image.reshape((image.shape[0] * image.shape[1], 3))

# Find and display most X dominant colors
cluster = KMeans(n_clusters=5).fit(reshape)
colors, visualize = visualize_colors(cluster, cluster.cluster_centers_)
visualize = cv2.cvtColor(visualize, cv2.COLOR_RGB2BGR)

# Obtain dominant RGB color code
dominant_color = colors[-1][1].tolist()
dominant_color_average = int(sum(dominant_color) / 3)
print('Dominant color:', dominant_color)
print('Dominant color average:', dominant_color_average)

# Find best color
if dominant_color_average <= 85:
    print('White!')
elif dominant_color_average > 85 and dominant_color_average <= 170:
    print('Gray!')
elif dominant_color_average > 170:
    print('Black!')

cv2.imshow('visualize', visualize)
cv2.waitKey()
Run Code Online (Sandbox Code Playgroud)