如何以编程方式计算两种颜色之间的对比度?


75

简单明了,取黄色和白色:

back_color = {r:255,g:255,b:255}; //white
text_color = {r:255,g:255,b:0}; //yellow

在上帝的地球上具有普遍常数的物理学定律,使得黄色文本无法在白色背景上读取,而蓝色文本却可以读取?

为了我的可自定义小部件,我尝试了所有可能的色彩模型,这些色彩模型为其找到了转换功能。仅基于数值比较,都不能说绿色可以在白色上,而黄色不能。

我查看了Adsense(由所有互联网的Budda创建),猜测他们做了什么,他们进行了预设和颜色单元的距离计算。我不能那样做 只要仍然可以阅读文本,我的用户甚至有权选择最强烈的视网膜发炎,不美观的组合。


因此,您的用户可以选择任何两种颜色,只要它们具有明显的对比度?
DanRedux 2012年

从技术的角度来看,我觉得这很有趣,但实际上,如果您的用户有“权利”选择任何颜色,为什么您甚至不在乎它是否可以被阅读?难道不是他们要正确吗?
nnnnnn 2012年

@nnnnnn我不太在乎他们选择什么颜色,它们可以随意混合,但是我关心(c)2012 Company Inc.的可读性。
Silviu-Marian 2012年

我设置了一个jsfiddle来亲自查看答案的准确性,并且他们确实能够很好地预测可读性:jsfiddle.net/UVUZC
mowwwalker 2012年

万一有人错过ShaoKahn的回应,这就是联系
Silviu-Marian 2012年

Answers:


97

根据Wikipedia的说法,当转换为亮度的灰度表示时,“一个人必须获得其红色,绿色和蓝色的值”,然后按下一个比例进行混合:R:30%G:59%B:11%

因此,白色将具有100%的亮度,而黄色将具有89%的亮度。同时,绿色的比例只有59%。11%几乎比41%的差异低四倍!

甚至连石灰(#00ff00)都不适合阅读大量文本。

恕我直言,具有良好对比度的颜色的亮度至少应相差50%。并且应该测量该亮度,以转换为灰度。

upd:最近在网上找到了一个全面的工具,该工具按顺序使用w3文档中的公式。 阈值可以从#1.4中获取。 这是此高级工具的实现。

function luminanace(r, g, b) {
    var a = [r, g, b].map(function (v) {
        v /= 255;
        return v <= 0.03928
            ? v / 12.92
            : Math.pow( (v + 0.055) / 1.055, 2.4 );
    });
    return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}
function contrast(rgb1, rgb2) {
    var lum1 = luminanace(rgb1[0], rgb1[1], rgb1[2]);
    var lum2 = luminanace(rgb2[0], rgb2[1], rgb2[2]);
    var brightest = Math.max(lum1, lum2);
    var darkest = Math.min(lum1, lum2);
    return (brightest + 0.05)
         / (darkest + 0.05);
}
contrast([255, 255, 255], [255, 255, 0]); // 1.074 for yellow
contrast([255, 255, 255], [0, 0, 255]); // 8.592 for blue
// minimal recommended contrast ratio is 4.5, or 3 for larger font-sizes

有关更多信息,请查看有关如何计算该值的WCAG 2.0文档


2
+1是的。在现实生活中-必须转换为灰度以检查设计的可读性(特别是徽标)。
Roko C. Buljan

我选择这个答案作为解决方案,因为它是最全面的解决方案,我不能选择两个解决方案。但是我会选择HondaSuzukiShaolinShaorma的答案,因为它提供了现成的代码。
Silviu-Marian 2012年

11
contrast([255, 255, 255], [0, 0, 255])返回8.592时,相同的数字反向contrast([0, 0, 255], [255, 255, 255])返回0.116 –即1 / 8.592。为了得到1和21之间的对比度,你将需要输出来划分1,如果输出为<1 function contrast(rgb1, rgb2) { var result = (luminanace(rgb1[0], rgb1[1], rgb1[2]) + 0.05) / (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05); if (result < 1) result = 1/result; return result; }
mattsoave

3
这是一个小错误:return (luminanace(rgb1[0], rgb1[1], rgb1[2]) + 0.05) / (luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05); 。您必须将较大的值除以较小的值。更好地使用此:l1=luminanace(rgb1[0], rgb1[1], rgb1[2]) + 0.05; l2=luminanace(rgb2[0], rgb2[1], rgb2[2]) + 0.05; return (Math.max(l1,l2) / Math.min(l1,l2));
zuluk

1
@zuluk,您完全正确。编辑。 w3.org/TR/WCAG20-TECHS/G17.html#G17-procedure
Bryan Rayner

26

有多种计算对比度的方法,但是一种常见的方法是此公式:

brightness = (299*R + 587*G + 114*B) / 1000

您对两种颜色都执行此操作,然后进行区别处理。显然,白底蓝色比白底黄色具有更大的对比度。



2
仅就FYI而言,上面显示的系数(299 * R + 587 * G + 114 * B)与NTSC有关,并且对于Web颜色(sRGB)已过时/不正确。正确的sRGB系数为0.2126 * R + 0.7152 * G + 0.0722 * B(请注意,对于sRGB,必须首先线性化RGB分量)。
Myndex '19

2

最近,我在此页面上找到了答案,并使用代码为Adobe Illustrator编写了一个脚本来计算对比度。

在这里您可以看到结果:http : //screencast.com/t/utT481Ut

上面脚本的一些速记符号使我感到困惑,而在Adobe扩展脚本中不起作用。因此,我认为可以分享我对kirilloid共享代码的改进/解释。

function luminance(r, g, b) {
    var colorArray = [r, g, b];
    var colorFactor;
    var i;
    for (i = 0; i < colorArray.length; i++) {
        colorFactor = colorArray[i] / 255;
        if (colorFactor <= 0.03928) {
            colorFactor = colorFactor / 12.92;
        } else {
            colorFactor = Math.pow(((colorFactor + 0.055) / 1.055), 2.4);
        }
        colorArray[i] = colorFactor;
    }
    return (colorArray[0] * 0.2126 + colorArray[1] * 0.7152 + colorArray[2] * 0.0722) + 0.05;
}

当然,您需要调用此函数

在for循环中,我从illustrator对象获得了所有颜色

//just a snippet here to demonstrate the notation
var selection = app.activeDocument.selection;
for (i = 0; i < selection.length; i++) {
   red[i] = selection[i].fillColor.red;
   //I left out the rest,because it would become to long
}

//this can then be used to calculate the contrast ratio.
var foreGround = luminance(red[0], green[0], blue[0]);
var background = luminance(red[1], green[1], blue[1]);
luminanceValue = foreGround / background;
luminanceValue = round(luminanceValue, 2);

//for rounding the numbers I use this function:
function round(number, decimals) {
   return +(Math.round(number + "e+" + decimals) + "e-" + decimals);
}

有关对比度的更多信息:http : //webaim.org/resources/contrastchecker/


1

根据kirilloid答案:

Angular Service将通过传入十六进制值来计算对比度和发光度:

.service('ColorContrast', [function() {
var self = this;

/**
 * Return iluminance value (base for getting the contrast)
 */
self.calculateIlluminance = function(hexColor) {
    return calculateIluminance(hexColor);
};

/**
 * Calculate contrast value to white
 */
self.contrastToWhite = function(hexColor){
    var whiteIlluminance = 1;
    var illuminance = calculateIlluminance(hexColor);
    return whiteIlluminance / illuminance;
};

/**
* Bool if there is enough contrast to white
*/
self.isContrastOkToWhite = function(hexColor){
    return self.contrastToWhite(hexColor) > 4.5;
};

/**
 * Convert HEX color to RGB
 */
var hex2Rgb = function(hex) {
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;
};

/**
 * Calculate iluminance
 */
var calculateIlluminance = function(hexColor) {
    var rgbColor = hex2Rgb(hexColor);
    var r = rgbColor.r, g = rgbColor.g, b = rgbColor.b;
    var a = [r, g, b].map(function(v) {
        v /= 255;
        return (v <= 0.03928) ?
            v / 12.92 :
            Math.pow(((v + 0.055) / 1.055), 2.4);
    });
    return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
};

}]);

0
module.exports = function colorcontrast (hex) {
    var color = {};

    color.contrast = function(rgb) {
        // check if we are receiving an element or element background-color
        if (rgb instanceof jQuery) {
            // get element background-color
            rgb = rgb.css('background-color');
        } else if (typeof rgb !== 'string') {
            return;
        }

        // Strip everything except the integers eg. "rgb(" and ")" and " "
        rgb = rgb.split(/\(([^)]+)\)/)[1].replace(/ /g, '');

        // map RGB values to variables
        var r = parseInt(rgb.split(',')[0], 10),
            g = parseInt(rgb.split(',')[1], 10),
            b = parseInt(rgb.split(',')[2], 10),
            a;

        // if RGBA, map alpha to variable (not currently in use)
        if (rgb.split(',')[3] !== null) {
            a = parseInt(rgb.split(',')[3], 10);
        }

        // calculate contrast of color (standard grayscale algorithmic formula)
        var contrast = (Math.round(r * 299) + Math.round(g * 587) + Math.round(b * 114)) / 1000;

        return (contrast >= 128) ? 'black' : 'white';
    };

    // Return public interface
    return color;

};

这看起来不错,但是为什么您的colorcontrast方法接受hex参数,但是不使用该参数呢?
ctlockey

0

const getLuminanace = (values) => {
  const rgb = values.map((v) => {
    const val = v / 255;
    return val <= 0.03928 ? val / 12.92 : ((val + 0.055) / 1.055) ** 2.4;
  });
  return Number((0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]).toFixed(3));
};

const getContrastRatio = (colorA, colorB) => {
  const lumA = getLuminanace(colorA);
  const lumB = getLuminanace(colorB);

  return (Math.max(lumA, lumB) + 0.05) / (Math.min(lumA, lumB) + 0.05);
};

// usage:
const back_color = [255,255,255]; //white
const text_color = [255,255,0]; //yellow

getContrastRatio(back_color, text_color); // 1.0736196319018405

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.