以编程方式选择高对比度颜色

29

这似乎是一个简单的问题,但我一直没有找到方法使其正常工作。

实际上,我有一个愚蠢的本地主页,我用它来进行网站开发。当我在开发服务器和我的 C# 代码的本地版本之间切换时(通过 host 文件从 dev url 转发),有时我会忘记“dev.foo.com”指向哪里 - 是本地还是服务器。

因此,我创建了一个页面作为我的默认网页的默认页面,在本地运行,这样我就可以轻松地将自己的本地主机与服务器区分开来。

这个页面会随机做很多事情(包括为 D&D 生成角色的起始统计数据),包括设置随机背景颜色。我是通过生成 3 个介于 0 和 255 之间的随机数,并将它们设置为 CSS 中 body 背景颜色的 RGB 值来实现这一点。

给定三个整数 R、G 和 B,如何确定 R2、G2 和 B2,使第二种颜色与第一种颜色形成高对比度?我喜欢让页面有随机背景颜色(这样可以防止我习惯了落地页的外观),但我也希望能够阅读文本。

11个回答

29

为了使文本易于阅读,需要有亮度差异,因为颜色视觉本身的分辨率太低。

因此,作为算法,我建议以下步骤:

  • 选择一个随机背景颜色。

  • 然后确定它是浅色还是深色。例如,您可以检查三原色的平均值是否大于或等于128。

  • 对于浅色使用黑色文本,对于深色使用白色文本。

更新:下面是我在玩Rust包plotterssplit_evenly示例时制作的示例图像,它显示了Palette99中的颜色:

7x3个彩色区域,它们的索引以黑色或白色显示,取决于背景颜色的亮度


这是一个相当简单的解决方案,而且(现在)运行得相当不错。我会尝试一段时间,因为它总是可读的,虽然有点平淡无奇。 - Jeff

8
“对比度”是一个有涵义的词。如果您只关心文本可读性,那么一种简单的方法是在基于亮度的颜色空间(如HSL)中工作,并选择具有较大亮度差异的前景和背景颜色。
HSL与RGB之间的转换是众所周知的 - 详见维基百科。
如果您谈论实际颜色对比度,则情况并不像刚才那么简单(我所知道的有许多感知因素尚未减少到单个颜色空间),但我认为您不需要那种复杂程度。

1
有一个处理HSL转换的类可以在这里找到:http://richnewman.wordpress.com/2007/05/07/using-hsl-color-hue-saturation-luminosity-to-create-better-looking-guis-part-2/ - KevB

5
您可以在Color类上使用GetBrightness()方法。它返回一个从0.0(黑色亮度)到1.0(白色)的浮点值。 一个简单的解决方案是:
var color1 = new Color.FromArgb(r1, g1, b1);
var brightness = color1.GetBrightness();

var color2 = brightness > 0.5 ? Color.Black : Color.White;

5

看看这个 PHP 解决方案:使用 PHP 计算颜色对比度,作者是 Andreas Gohr。当然,它也可以移植到任何语言中。

他还有一个非常好的对比度分析演示,你可以找到一些最小的对比度水平来使用。


实际上,只有第三个函数处理颜色对比度,第一和第二个函数计算一些随机指标。 - Premature Optimization

3

我曾在Palm OS应用程序中做过类似的事情。这是我的解决方案。它不能提供“高对比度”颜色,但是背景颜色与文本颜色足够不同,以便阅读:

  // Black background is a special case.  It's fairly likely to occur and 
  // the default color shift we do isn't very noticeable with very dark colors.
  if (backColor.r < 0x20 && backColor.g < 0x20 && backColor.b < 0x20)
  {
      textColor.r = backColor.r + 0x20;
      textColor.g = backColor.g + 0x20;
      textColor.b = backColor.b + 0x20;
  }
  else
  {
      textColor.r = backColor.r + ((backColor.r < 128) ? 0x10 : -0x10);
      textColor.g = backColor.g + ((backColor.g < 128) ? 0x10 : -0x10);
      textColor.b = backColor.b + ((backColor.b < 128) ? 0x10 : -0x10);
  }

您可能不需要将黑色作为特殊情况考虑,因为Palm的颜色处理有点奇怪(16位色)。


3

这些答案或多或少都建议基于颜色是明亮还是暗淡,使用两种或三种颜色之一。

我采用了一种稍微不同的方法,在我的情况下它很优雅。以下是实现。

 int color = your_color;
 contrastColor = Color.rgb(255-(color >> 16)&0xFF, 255-(color >> 8)&0xFF, 255- color&0xFF);

这很简单也很棒。


嗨,Mayank刚刚发送了一封电子邮件,关于一个潜在的咨询工作,如果你有兴趣的话! - Crashalot
我认为这是相同的方法: Color.rgb(255 - Color.red(color), 255 - Color.green(color), 255 - Color.blue(color) - Osvel Alvarez Jacomino

2

如果你翻转所有位,你会得到“相反”的颜色,这将产生很好的对比效果。

我认为在C#中,这是~运算符:

R2 = ~R1;
G2 = ~G1;
B2 = ~B1;

6
对于颜色直方图中位于“中间”位置的颜色,比如 #808080,这种方法效果不佳。 - Tim Lesher
没错...但是相反的颜色放在一起时,还有一个额外的好处,就是看起来很舒服。 - bobwienholt
11
Tim,你可以更加强烈地表达——它根本不起作用。 :-) - mhenry1384
1
你可以为那些“中间”情况添加一些“if”语句,以便它能够正确地工作。你也可以像这样编写代码:“如果(亮度)返回黑色;否则返回白色;”,但这显然会减少输出颜色的数量。 - luiscubal
也许我应该指出这是一个ASP网站,但你的评论仍然很有帮助。 我唯一的问题在于中心的这些'边缘'情况,以及随之而来的问题:'离中心多近才能切换到黑白?' - Jeff

1
感谢@starblue!以下是我使用的C#代码。
 public static string GetContrastBlackOrWhiteColorAsHtmlColorCode(Color c)
        {
            System.Drawing.Color color = System.Drawing.ColorTranslator.FromHtml("transparent");

            try
            {
                if (c.R >= 128 && c.G >= 128 && c.B >= 128)
                {
                    return System.Drawing.ColorTranslator.ToHtml(Color.Black);
                }
                else
                {
                    return System.Drawing.ColorTranslator.ToHtml(Color.White);
                }
            }
            catch (Exception)
            {
            }

            return System.Drawing.ColorTranslator.ToHtml(color);
        }

2
这不完全是StarBlue所说的 - 他建议对R、G和B值取平均值。你正在设置最小阈值。你的代码将在#FF7FFF#7FFFFF#FFFF7F中使用白色。 - Jeff

0

灰度转换最好使用特殊系数进行计算,例如Gray = (Red * 0.3 + Green * 0.59 + Blue * 0.11)。更多细节可以在这里找到。然后您需要将该值与128进行比较,并根据第一条评论中所示选择白色或黑色。


0

为了获得最佳对比效果,请使用以下代码

function lumdiff($R1,$G1,$B1,$R2,$G2,$B2){

    $L1 = 0.2126 * pow($R1/255, 2.2) +
          0.7152 * pow($G1/255, 2.2) +
          0.0722 * pow($B1/255, 2.2);

    $L2 = 0.2126 * pow($R2/255, 2.2) +
          0.7152 * pow($G2/255, 2.2) +
          0.0722 * pow($B2/255, 2.2);

    if($L1 > $L2){
        return ($L1+0.05) / ($L2+0.05);
    }else{
        return ($L2+0.05) / ($L1+0.05);
    }
}

function get_the_contrast($c1, $c2) {
    return (lumdiff(hexdec(substr($c1,0,2)),
        hexdec(substr($c1,2,2)),hexdec(substr($c1,4,2)),
        hexdec(substr($c2,0,2)),hexdec(substr($c2,2,2)),
        hexdec(substr($c2,4,2))));
}

上面的方法 ( AVG(red,green,blue) > 128 ) 不是很好。


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