【问题标题】:Increase or decrease color saturation增加或减少色彩饱和度
【发布时间】:2012-11-28 04:37:51
【问题描述】:

我想知道增加或减少一种 RGB 颜色饱和度的算法

例如,如果我的颜色为 rgb(200, 30, 40)(红色),那么函数存根将是

function Saturation(color, factor)
where color.r = 200, color.g= 30 and color.b=40

任何人都知道一个库或有一个代码 sn-p 可以做到这一点?

【问题讨论】:

标签: javascript algorithm colors


【解决方案1】:

根据我提出的 Bali Balo 建议:

RGBtoHSV= function(color) {
        var r,g,b,h,s,v;
        r= color[0];
        g= color[1];
        b= color[2];
        min = Math.min( r, g, b );
        max = Math.max( r, g, b );


        v = max;
        delta = max - min;
        if( max != 0 )
            s = delta / max;        // s
        else {
            // r = g = b = 0        // s = 0, v is undefined
            s = 0;
            h = -1;
            return [h, s, undefined];
        }
        if( r === max )
            h = ( g - b ) / delta;      // between yellow & magenta
        else if( g === max )
            h = 2 + ( b - r ) / delta;  // between cyan & yellow
        else
            h = 4 + ( r - g ) / delta;  // between magenta & cyan
        h *= 60;                // degrees
        if( h < 0 )
            h += 360;
        if ( isNaN(h) )
            h = 0;
        return [h,s,v];
    };

HSVtoRGB= function(color) {
        var i;
        var h,s,v,r,g,b;
        h= color[0];
        s= color[1];
        v= color[2];
        if(s === 0 ) {
            // achromatic (grey)
            r = g = b = v;
            return [r,g,b];
        }
        h /= 60;            // sector 0 to 5
        i = Math.floor( h );
        f = h - i;          // factorial part of h
        p = v * ( 1 - s );
        q = v * ( 1 - s * f );
        t = v * ( 1 - s * ( 1 - f ) );
        switch( i ) {
            case 0:
                r = v;
                g = t;
                b = p;
                break;
            case 1:
                r = q;
                g = v;
                b = p;
                break;
            case 2:
                r = p;
                g = v;
                b = t;
                break;
            case 3:
                r = p;
                g = q;
                b = v;
                break;
            case 4:
                r = t;
                g = p;
                b = v;
                break;
            default:        // case 5:
                r = v;
                g = p;
                b = q;
                break;
        }
        return [r,g,b];
    }

通过转换为 HSV(色相、饱和度和值)格式,您可以通过以下方式手动更改 S 分量:

var hsv= RGBtoHSV ([200,100,100]);
alert(hsv)
hsv[1] *= 1.5;
alert(hsv)
var rgb= HSVtoRGB(hsv);
alert(rgb); //new color

【讨论】:

  • 谢谢!对于它的价值,为了让它与我的程序一起工作,我必须添加一个 if ( isNaN(h) ) { h = 0;在 toHsv 函数的末尾检查,并在 toRgb 函数中返回之前对 r、g、b 值进行 Math.floor'ing。 (也许这是我的程序特有的东西。)
  • @PhilippLenssen 嘿,我将编辑我的答案,但我最终使用了这个库:purl.eligrey.com/github/color.js/blob/master/color.js,主要是因为它可以解析像“#FFFFFF”和“rgb(255,255,255)”这样的字符串。
【解决方案2】:

这是一种快速而肮脏的方式,可能在任何技术上都不正确,但与转换为 HSV 并返回相比,它所涉及的计算量更少(因此如果重要的话,渲染速度更快):

灰度相当于计算像素 RGB 部分的平均亮度。我们可以通过对灰色部分和彩色部分应用值加权来混合灰度:

var pixels = context.getImageData(0, 0, canvas.width, canvas.height);
grayscale = function (pixels, value) {
    var d = pixels.data;
    for (var i = 0; i < d.length; i += 4) {
        var r = d[i];
        var g = d[i + 1];
        var b = d[i + 2];
        var gray = 0.2989*r + 0.5870*g + 0.1140*b; //weights from CCIR 601 spec
        d[i] = gray * value + d[i] * (1-value);
        d[i+1] = gray * value + d[i+1] * (1-value);
        d[i+2] = gray * value + d[i+2] * (1-value);
    }
    return pixels;
};

因此,我们可以将其移除并将相关颜色重新添加到“饱和度”中,而不是添加“灰度”:

saturate = function (pixels, value) {
    var d = pixels.data;
    for (var i = 0; i < d.length; i += 4) {
        var r = d[i]; 
        var g = d[i + 1];
        var b = d[i + 2];
        var gray = 0.2989*r + 0.5870*g + 0.1140*b; //weights from CCIR 601 spec
        d[i] = -gray * value + d[i] * (1+value);
        d[i+1] = -gray * value + d[i+1] * (1+value);
        d[i+2] = -gray * value + d[i+2] * (1+value);
        //normalize over- and under-saturated values
        if(d[i] > 255) d[i] = 255;
        if(d[i+1] > 255) d[i] = 255;
        if(d[i+2] > 255) d[i] = 255;
        if(d[i] < 0) d[i] = 0;
        if(d[i+1] < 0) d[i] = 0;
        if(d[i+2] < 0) d[i] = 0;
    }
    return pixels;
};

再次声明,这种“看起来”饱和但可能绝不符合“饱和”的技术定义(无论是什么);特此贴出来,希望对路人有帮助。

【讨论】:

【解决方案3】:

// 我的解决方案是 HEX 格式的颜色。您可以按百分比请求饱和度。

function applySaturationToHexColor(hex, saturationPercent) {
    if (!/^#([0-9a-f]{6})$/i.test(hex)) {
        throw('Unexpected color format');
    }

    if (saturationPercent < 0 || saturationPercent > 100) {
        throw('Unexpected color format');
    }

    var saturationFloat   = saturationPercent / 100,
        rgbIntensityFloat = [
            parseInt(hex.substr(1,2), 16) / 255,
            parseInt(hex.substr(3,2), 16) / 255,
            parseInt(hex.substr(5,2), 16) / 255
        ];

    var rgbIntensityFloatSorted = rgbIntensityFloat.slice(0).sort(function(a, b){ return a - b; }),
        maxIntensityFloat       = rgbIntensityFloatSorted[2],
        mediumIntensityFloat    = rgbIntensityFloatSorted[1],
        minIntensityFloat       = rgbIntensityFloatSorted[0];

    if (maxIntensityFloat == minIntensityFloat) {
        // All colors have same intensity, which means 
        // the original color is gray, so we can't change saturation.
        return hex;
    }

    // New color max intensity wont change. Lets find medium and weak intensities.
    var newMediumIntensityFloat,
        newMinIntensityFloat = maxIntensityFloat * (1 - saturationFloat);

    if (mediumIntensityFloat == minIntensityFloat) {
        // Weak colors have equal intensity.
        newMediumIntensityFloat = newMinIntensityFloat;
    }
    else {
        // Calculate medium intensity with respect to original intensity proportion.
        var intensityProportion = (maxIntensityFloat - mediumIntensityFloat) / (mediumIntensityFloat - minIntensityFloat);
        newMediumIntensityFloat = (intensityProportion * newMinIntensityFloat + maxIntensityFloat) / (intensityProportion + 1);
    }

    var newRgbIntensityFloat       = [],
        newRgbIntensityFloatSorted = [newMinIntensityFloat, newMediumIntensityFloat, maxIntensityFloat];

    // We've found new intensities, but we have then sorted from min to max.
    // Now we have to restore original order.
    rgbIntensityFloat.forEach(function(originalRgb) {
        var rgbSortedIndex = rgbIntensityFloatSorted.indexOf(originalRgb);
        newRgbIntensityFloat.push(newRgbIntensityFloatSorted[rgbSortedIndex]);
    });

    var floatToHex = function(val) { return ('0' + Math.round(val * 255).toString(16)).substr(-2); },
        rgb2hex    = function(rgb) { return '#' + floatToHex(rgb[0]) + floatToHex(rgb[1]) + floatToHex(rgb[2]); };

    var newHex = rgb2hex(newRgbIntensityFloat);

    return newHex;
}

【讨论】:

  • 对于像我这样的傻瓜,是否愿意在实际的颜色处理部分(一旦完成所有解析逻辑)添加 cmets?
  • @Escher,重命名 vars 以使代码不言自明,还添加了一些 cmets,最后一部分也有所改进。让我知道它是否符合您的需要。
猜你喜欢
  • 1970-01-01
  • 2020-11-30
  • 2019-02-06
  • 2014-12-06
  • 2017-12-26
  • 2015-12-24
  • 1970-01-01
  • 2012-08-02
  • 1970-01-01
相关资源
最近更新 更多