What is the efficient way to calculate human eye contrast difference for RGB values?

前端 未结 5 1122
太阳男子
太阳男子 2020-12-11 09:35

In order to check if two colors in grayscale will be too close to be distinguished by the human eye. I want to be able to generate a warning to the user if \'dangerous\' col

5条回答
  •  无人及你
    2020-12-11 09:50

    Just for a better syntax and ease of use I have put the whole theory into one single parser within an object that runs as follows.

    The parser will compute these values in one step from color 318261:

    A returned object will look like:

    hex: "#318261"
    rgb: {
      r: 49,
      g: 130,
      b: 97
    }
    int: 10313648
    dec: {
      r: 0.19215686274509805,
      g: 0.5098039215686274,
      b: 0.3803921568627451
    }
    lin: {
      r: 0.030713443732993635,
      g: 0.2232279573168085,
      b: 0.11953842798834562
    }
    y: 0.17481298771137443
    lstar: 48.86083783595441
    

    JavaScript can call the object internal parser with a Hex color string as a parameter. The hex string can look like either 000 or #000 or 000000 or #000000. There are two ways to process the result.

    A: take the returned object as a whole into a variable:

    var result = Color_Parser.parseHex('318261');
    var lstar = result.lstar;
    

    B: parse once and thereafter access parts of the last parser result. For example pick only the the L* contrast value would be just:

    Color_Parser.parseHex('#ABC');
    var lstar = Color_Parser.result.lstar;
    

    Here is the full code:

    const Color_Parser = {
      version: '1.0.0.beta',
      name: 'Color_Parser',
      result: null, // the parser output
      loging: true, // set to false to disable writing each step to console log
      parseHex: function(_input) {
        if (this.loging) {
          console.log(this.name + ', input: ' + _input);
        }
        this.result = {};
        // pre flight checks
        if (!_input) {
          this.result.error = true;
          console.log(this.name + ', error');
          return this.result;
        }
        // first convert shorthand Hex strings to full strings
        this.result.hex = String(_input);
        if (this.result.hex.length == 3) {
          this.result.hex = '#' + this.result.hex.substr(0, 1) + this.result.hex.substr(0, 1) + this.result.hex.substr(1, 1) + this.result.hex.substr(1, 1) + this.result.hex.substr(2, 1) + this.result.hex.substr(2, 1);
        }
        if (this.result.hex.length == 4) {
          this.result.hex = '#' + this.result.hex.substr(1, 1) + this.result.hex.substr(1, 1) + this.result.hex.substr(2, 1) + this.result.hex.substr(2, 1) + this.result.hex.substr(3, 1) + this.result.hex.substr(3, 1);
        }
        if (this.result.hex.length == 6) {
          this.result.hex = '#' + this.result.hex;
        }
        if (this.loging) {
          console.log(this.name + ', added to result: ' + this.result.hex);
        }
        // second get int values from the string segments as channels
        this.result.rgb = {
          r: null,
          g: null,
          b: null
        };
        this.result.rgb.r = parseInt(this.result.hex.substr(1, 2), 16);
        this.result.rgb.g = parseInt(this.result.hex.substr(3, 2), 16);
        this.result.rgb.b = parseInt(this.result.hex.substr(5, 2), 16);
        if (this.loging) {
          console.log(this.name + ', added to result: ' + this.result.rgb);
        }
        // third get the combined color int value
        this.result.int = ((this.result.rgb.r & 0x0ff) << 16) | ((this.result.rgb.g & 0x0ff) << 8) | (this.result.rgb.b & 0x0ff);
        if (this.loging) {
          console.log(this.name + ', added to result: ' + this.result.int);
        }
        // fourth turn 8 bit channels to decimal
        this.result.dec = {
          r: null,
          g: null,
          b: null
        };
        this.result.dec.r = this.result.rgb.r / 255.0; // red channel to decimal
        this.result.dec.g = this.result.rgb.g / 255.0; // green channel to decimal
        this.result.dec.b = this.result.rgb.b / 255.0; // blue channel to decimal
        if (this.loging) {
          console.log(this.name + ', added to result: ' + this.result.dec);
        }
        // fifth linearize each channel
        this.result.lin = {
          r: null,
          g: null,
          b: null
        };
        for (var i = 0, len = 3; i < len; i++) {
          if (this.result.dec[['r', 'g', 'b'][i]] <= 0.04045) {
            this.result.lin[['r', 'g', 'b'][i]] = this.result.dec[['r', 'g', 'b'][i]] / 12.92;
          } else {
            this.result.lin[['r', 'g', 'b'][i]] = Math.pow(((this.result.dec[['r', 'g', 'b'][i]] + 0.055) / 1.055), 2.4);
          }
        }
        if (this.loging) {
          console.log(this.name + ', added to result: ' + this.result.lin);
        }
        // get Y from linear result
        this.result.y = (0.2126 * (this.result.lin.r)); // red channel
        this.result.y += (0.7152 * (this.result.lin.g)); // green channel
        this.result.y += (0.0722 * (this.result.lin.b)); // blue channel
        if (this.loging) {
          console.log(this.name + ', added to result: ' + this.result.y);
        }
        // get L* contrast from Y 
        if (this.result.y <= (216 / 24389)) {
          this.result.lstar = this.result.y * (24389 / 27);
        } else {
          this.result.lstar = Math.pow(this.result.y, (1 / 3)) * 116 - 16;
        }
        if (this.loging) {
          console.log(this.name + ', added to result: ' + this.result.lstar);
        }
        // compute grayscale is to be continued hereafter
        // compute inverted rgb color
        this.result.invert = {
          r: null,
          g: null,
          b: null,
          hex: null
        };
        this.result.invert.r = (255 - this.result.rgb.r);
        this.result.invert.g = (255 - this.result.rgb.g);
        this.result.invert.b = (255 - this.result.rgb.b);
        // reverse compute hex from inverted rgb          
        this.result.invert.hex = this.result.invert.b.toString(16); // begin with blue channel
        if (this.result.invert.hex.length < 2) {
          this.result.invert.hex = '0' + this.result.invert.hex;
        }
        this.result.invert.hex = this.result.invert.g.toString(16) + this.result.invert.hex;
        if (this.result.invert.hex.length < 4) {
          this.result.invert.hex = '0' + this.result.invert.hex;
        }
        this.result.invert.hex = this.result.invert.r.toString(16) + this.result.invert.hex;
        if (this.result.invert.hex.length < 6) {
          this.result.invert.hex = '0' + this.result.invert.hex;
        }
        this.result.invert.hex = '#' + this.result.invert.hex;
        this.result.error = false;
        if (this.loging) {
          console.log(this.name + ', final output:');
        }
        if (this.loging) {
          console.log(this.result);
        }
        return this.result;
      }
    }
    

提交回复
热议问题