TSLtoRGB色彩空间转换

时间:2017-04-29 14:49:23

标签: math color-space

有人知道RGBtoTSL的正确公式吗?

维基百科说RGB和TSL之间的转换是这样的: enter image description here

但....维基百科https://en.wikipedia.org/wiki/TSL_color_space上呈现的逆向转换是不正确的。他们的还原公式所呈现的结果是不正确的。

恢复转型的正确公式是什么?即:从TSL转换为RGB?

Serg,以下是我们尝试恢复操作的一些尝试:) Asm中的示例(RosAsm汇编程序)。

; used variables
[Float_YIQ_Red_M1: R$ 0.29889531
Float_YIQ_Green_M2: R$  0.58662247
Float_YIQ_Blue_M3: R$  0.11448223]
[Float_minusOneThird: R$ (-1/3)] ; error in RosAsm. It can´ see "-(1/3)"
[r1Factor: R$ 0]
[g1Factor: R$ 0]
[rFactor: R$ 0]
[gFactor: R$ 0]
[TmpFloat: R$ 0]
[Float_Var95: R$ (9/5)]
[Float_Var14: R$ (1/4)]
[Float_Var34: R$ (3/4)]
[Float_VarOne2Pi: R$ 0.159154943091895335768883763372514362034459645740456448747] ; 1/(2*pi)
[xFactor: R$ 0]
[kFactor: R$ 0]
[Float_Half: R$ 0.5]
[Float_Var59: R$ (5/9)]
[Float_Var53SquareRoot: R$ 0.745355992499929898803057889577092078480206119870508574756] ; sqrt(5)/3
[Float_OneThird: R$ (1/3)]
[FloatTSLVar1: R$ 0.184413]
[FloatTSLVar2: R$ 0.4721403]
[Float_TempRed: R$ 0]
[Float_TempGreen: R$ 0]
[Float_TempBlue: R$ 0]
[Float_Two_PI: R$ 6.283185307179586476925286766559005768394338798750211641948]
[Float_Five: R$ 5.0]
; -----------------------------------------------------------
Proc RGBtoTSL2a:
    Arguments @PixelSrc, @Tint, @Saturation, @Light
    Local @TempRed, @TempGreen, @TempBlue, @TempCmax, @TempCmin, @TempDelta_Max, @TempVar
    Uses eax, ecx, esi, edi, edx, ebx

finit
; RGB from 0 to 255
; get RED
mov esi D@PixelSrc | movzx edi B$esi+ARGB.RedDis | lea ecx D@TempRed | mov D$ecx edi | mov eax edi
; get GREEN
mov esi D@PixelSrc | movzx edi B$esi+ARGB.GreenDis | lea ecx D@TempGreen | mov D$ecx edi | add eax edi
; get BLUE
mov esi D@PixelSrc | movzx edi B$esi+ARGB.BlueDis | lea ecx D@TempBlue | mov D$ecx edi | add eax edi

If eax = 0
    fldz
    mov esi D@Light | fst R$esi
    mov esi D@Saturation | fst R$esi
    mov esi D@Tint | fstp R$esi
    ExitP
End_If

; Get Min and Max of RGB (just to get MIn/Max RGB to check for grey
lea ebx D@TempCmin | mov D$ebx 0
lea eax D@TempCmax | mov D$eax 0
call GetRGB_MinMax esi, ebx, eax

; Get Delta
; max = edx
; min = ecx
mov edx D@TempCmax | lea eax D@TempCmax | mov D$eax edx
mov ecx D@TempCmin | lea eax D@TempCmin | mov D$eax ecx
lea ebx D@TempDelta_Max | mov D$ebx 0; cmax-cmin
mov eax D@TempCmax | sub eax D@TempCMin | mov D$ebx eax

; 1st compute Light (Normalized)
mov esi D@Light
fild F@TempRed | fmul R$Float_YIQ_Red_M1
fild F@TempGreen | fmul R$Float_YIQ_Green_M2
faddp ST1 ST0
fild F@TempBlue | fmul R$Float_YIQ_Blue_M3
faddp ST1 ST0 | fmul R$FloatOne_255 | fstp R$esi

..If D@TempDelta_Max = 0; This is gray, no tint, no saturation
    fldz
    mov esi D@Saturation | fst R$esi
    mov esi D@Tint | fstp R$esi
..Else
    ; 2nd compute Saturation

    ; get rfactor
    lea edi D@TempVar
    fild F@TempRed | fiadd F@TempGreen | fiadd F@TempBlue | fistp F$edi

    fild F@TempRed | fidiv F$edi | fadd R$Float_minusOneThird | fstp R$rFactor

    ; get gfactor
    fild F@TempGreen | fidiv F$edi | fadd R$Float_minusOneThird | fstp R$gFactor

    mov esi D@Saturation
    fld R$rFactor | fmul ST0 ST0
    fld R$gFactor | fmul ST0 ST0
    faddp ST1 ST0 | fmul R$Float_Var95
    fsqrt | fstp R$esi

    ; 3rd compute Tint

    mov esi D@Tint
    fldz | fstp R$esi
    fld R$rFactor | fdiv R$gFactor | fstp R$TmpFloat

    Fpu_If R$gFactor > R$Float_Zero
        fld R$TmpFloat | fld1 | fpatan | fmul R$Float_VarOne2Pi | fadd R$Float_Var14 | fstp R$esi ; result in radian
    Fpu_Else_If R$gFactor < R$Float_Zero
        fld R$TmpFloat | fld1 | fpatan | fmul R$Float_VarOne2Pi | fadd R$Float_Var34 | fstp R$esi ; result in radian
    Fpu_End_If

..End_If

EndP

和恢复的功能

Proc TSLtoRGB2a:
    Arguments @Tint, @Saturation, @Light, @Red, @Green, @Blue
    Local @TempRed, @TempGreen, @TempBlue
    Uses eax, ecx, esi, edi, ebx


; check for light conditions
mov esi D@Light
Fpu_If R$esi = R$Float_Zero
    mov edi D@Red | mov D$edi 0
    mov edi D@Green | mov D$edi 0
    mov edi D@Blue | mov D$edi 0
    ExitP
Fpu_End_If

mov edi D@Saturation
mov esi D@Tint
;.Fpu_If_Or R$esi = R$Float_Zero, R$edi = R$Float_Zero ; Grey color found
; Not true. Tint = 0 not necessarily means grey. What means grey is 
; saturation = 0
; see Serg examples:(44, 22, 0) and (0, 32, 64)

.Fpu_If R$edi = R$Float_Zero ; Grey color found. Seems to be only this case for finding grey.
    mov ebx D@Light
    fld R$ebx | fst R$Float_TempRed | fst R$Float_TempGreen | fstp R$Float_TempBlue
    fld1 | fstp R$kFactor
.Fpu_Else
    ; compute -1 * cot(2*pi*Tint)
    fld R$esi
    fmul R$Float_Two_PI
    fptan
    fdivrp ST0 ST1
    fmul R$Float_Minus_one
    fstp R$xFactor

    fld1 | fld R$xFactor | fmul ST0 ST0 | fadd R$Float_One | fdivp ST0 ST1 | fmul R$Float_Five
    fsqrt | fmul R$Float_OneThird | fmul R$edi | fstp R$g1Factor
    Fpu_If R$esi > R$Float_Half
        fld R$g1Factor | fmul R$Float_Minus_One  | fstp R$g1Factor
    Fpu_End_If

    fld R$g1Factor | fmul R$xFactor | fstp R$r1Factor

    fld R$r1Factor | fadd R$Float_OneThird | fstp R$Float_TempRed
    fld R$g1Factor | fadd R$Float_OneThird | fstp R$Float_TempGreen
    fld1 | fsub R$Float_TempRed | fsub R$Float_TempGreen | fstp R$Float_TempBlue

    ; Compte KFactor
    mov ebx D@Light
    fld R$ebx
    fld R$Float_TempRed | fmul R$FloatTSLVar1
    fld R$Float_TempGreen | fmul R$FloatTSLVar2
    faddp ST1 ST0 | fadd R$Float_YIQ_Blue_M3
    fdivp ST1 ST0
    fstp R$kFactor

.Fpu_End_If

mov edi D@Red | fld R$kFactor | fmul R$Float_TempRed | fmul R$Float255 | fistp F$edi
If D$edi <s 0
    mov D$edi 0
Else_If D$edi > 255
    mov D$edi 255
End_If
mov edi D@Green | fld R$kFactor | fmul R$Float_TempGreen | fmul R$Float255 | fistp F$edi
If D$edi <s 0
    mov D$edi 0
Else_If D$edi > 255
    mov D$edi 255
End_If
mov edi D@Blue | fld R$kFactor | fmul R$Float_TempBlue | fmul R$Float255 | fistp F$edi
If D$edi <s 0
    mov D$edi 0
Else_If D$edi > 255 ; something still is incorrect.
                    ; See what happens when:
                    ; Tint = 0.85764542031
                    ; Saturation: 0.0315199340953953
                    ; Light = 0.8826082107058823529411764705882352941176

    mov D$edi 255
End_If

EndP

1 个答案:

答案 0 :(得分:1)

代码已更新并带有“负零”技巧来处理T = 0案例

评论太大了所以我会把它作为答案。我认为维基上的公式是正确的,除了2 * G == R + B之外的情况下工作正常(即除了g' == 0或换句话说T == 0),在这种情况下,向后转换在数学上不是唯一的。但实际上,因为我们的硬件上都有positive and negative zero,我们可以使用它们来区分这些不良情况。

这几乎是将该公式转换为JS的文字:

function rgbFromTsl(T, S, L) {
    if (arguments.length == 1) {
        T = arguments[0][0];
        S = arguments[0][1];
        L = arguments[0][2];
    }

    if (L == 0)
        return [0, 0, 0];

    var r1, g1, x;
    var r, g, b;

    // For T == 0 reverse solution is not unique
    // and we use a trick with "negative zero" to distinguish them
    if (isNegativeZero(T)) {
        g1 = 0;
        r1 = -Math.sqrt(5.0) / 3.0 * S;

    }
    else if (T == 0) {
        g1 = 0;
        r1 = Math.sqrt(5.0) / 3.0 * S;
    }
    else {

        x = -1.0 / Math.tan(2 * Math.PI * T);
        g1 = Math.sqrt(5.0 / (1 + x * x)) / 3.0 * S;
        if (T > 0.5)
            g1 = -g1;
        r1 = x * g1;
    }
    r = r1 + 1.0 / 3;
    g = g1 + 1.0 / 3;
    b = (1 - r - g);
    var k = L / (0.185 * r + 0.473 * g + 0.114);
    return [k * r, k * g, k * b];
}

以及您提供的示例

  

尝试输入:红色= 128,绿色= 22,蓝色= 37,一旦找到TSL,尝试反向公式,你会发现它产生了不正确的值(其中一个为负,顺便说一句,这是不可能的RGB)。

它似乎以非常精确的方式产生原始的R,G,B值。

这是一个完整的可运行代码,您可以在适当的位置运行并使用某些值。如果您发现除2*G = R+B又名T = 0

之外的其他一些不良案例,请与我们联系

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>TSL/RGB</title>
    <style>
        div {
            padding: 5px;
        }

        #container {
            width: 550px;
        }
        
        input {
            width: 150px;
        }

        #lc, #rc {
            width: 220px;
            border: dashed;
        }

        #lc {
            float: left;
        }

        #rc {
            float: right;
        }
    </style>
</head>
<body>
<div id="container">
    <div id="lc">
        <h1>RGB to TSL</h1>

        <form name="RGBsrc">
            <div id="RGBsrc">
                <div>
                    <label for="Rsrc">R=</label> <input type="number" id="Rsrc" value="128">
                </div>
                <div>
                    <label for="Gsrc">G=</label> <input type="number" id="Gsrc" value="22">
                </div>
                <div>
                    <label for="Bsrc">B=</label> <input type="number" id="Bsrc" value="37">
                </div>
            </div>
            <div id="TSLdst">

                <div>
                    <label for="Tdst">T=</label> <input type="number" id="Tdst" disabled="disabled">
                </div>
                <div>
                    <label for="Sdst">S=</label> <input type="number" id="Sdst" disabled="disabled">
                </div>
                <div>
                    <label for="Ldst">L=</label> <input type="number" id="Ldst" disabled="disabled">
                </div>
            </div>


            <div><button id="btnRgbToTsl">RGB to TSL</button></div>
            <div><button id="btnCopyTsl">Copy result to TSL</button></div>
        </form>
    </div>
    <div id="rc">
        <h1>TSL to RGB</h1>

        <form name="TSLsrc">
            <div id="TSLsrc">
                <div>
                    <label for="Tsrc">T=</label> <input type="number" id="Tsrc" value="0.5876633198969483">
                </div>
                <div>
                    <label for="Ssrc">S=</label> <input type="number" id="Ssrc" value="0.552900835318196">
                </div>
                <div>
                    <label for="Lsrc">L=</label> <input type="number" id="Lsrc" value="55.403999999999996">
                </div>
            </div>
            <div id="RGBdst">

                <div>
                    <label for="Rdst">R=</label> <input type="number" id="Rdst" disabled="disabled">
                </div>
                <div>
                    <label for="Gdst">G=</label> <input type="number" id="Gdst" disabled="disabled">
                </div>
                <div>
                    <label for="Bdst">B=</label> <input type="number" id="Bdst" disabled="disabled">
                </div>
            </div>
            <div>
                <label for="roundRgbDst">Round RGB</label> <input type="checkbox" id="roundRgbDst" checked="checked" style="width: 50px;" >
            </div>

            <button id="btnTslToRgb">TSL to RGB</button>

        </form>
    </div>
</div>
<script>

function isNegativeZero(v) {
    return 1 / v === -Infinity;
}

function floatToStrSmart(v) {
    if (isNegativeZero(v))
        return "-0.0";
    else
        return "" + v;
}

function parseFloatSmart(s) {
    if ((s === "-0") || (s === "-0.0"))
        return -0.0;
    else
        return parseFloat(s)

}

function tslFromRgb(R, G, B) {
    if (arguments.length == 1) {
        R = arguments[0][0];
        G = arguments[0][1];
        B = arguments[0][2];
    }
    if ((R == 0) && (G == 0) && (B == 0)) {
        return [0, 0, 0];
    }
    var L = 0.299 * R + 0.587 * G + 0.114 * B;
    var r1 = R / (R + G + B) - 1.0 / 3;
    var g1 = G / (R + G + B) - 1.0 / 3;

    var S = Math.sqrt(9.0 / 5 * (r1 * r1 + g1 * g1));
    var T;
    if (g1 == 0) {
        if (R < B)
            T = -0.0;
        else
            T = 0.0
    }
    else {
        T = Math.atan(r1 / g1) / Math.PI / 2 + 0.25;
        if (g1 < 0)
            T += 0.5;
    }

    return [T, S, L]
}


function rgbFromTsl(T, S, L) {
    if (arguments.length == 1) {
        T = arguments[0][0];
        S = arguments[0][1];
        L = arguments[0][2];
    }

    if (L == 0)
        return [0, 0, 0];

    var r1, g1, x;
    var r, g, b;

    // For T == 0 reverse solution is not unique
    // and we use a trick with "negative zero" to distinguish them
    if (isNegativeZero(T)) {
        g1 = 0;
        r1 = -Math.sqrt(5.0) / 3.0 * S;

    }
    else if (T == 0) {
        g1 = 0;
        r1 = Math.sqrt(5.0) / 3.0 * S;
    }
    else {

        x = -1.0 / Math.tan(2 * Math.PI * T);
        g1 = Math.sqrt(5.0 / (1 + x * x)) / 3.0 * S;
        if (T > 0.5)
            g1 = -g1;
        r1 = x * g1;
    }
    r = r1 + 1.0 / 3;
    g = g1 + 1.0 / 3;
    b = (1 - r - g);
    var k = L / (0.185 * r + 0.473 * g + 0.114);
    return [k * r, k * g, k * b];
}

document.getElementById('btnRgbToTsl').addEventListener('click', function (e) {
    e.preventDefault();
    var r = parseInt(document.getElementById('Rsrc').value);
    var g = parseInt(document.getElementById('Gsrc').value);
    var b = parseInt(document.getElementById('Bsrc').value);

    document.getElementById('Rsrc').value = r;
    document.getElementById('Gsrc').value = g;
    document.getElementById('Bsrc').value = b;

    var tsl = tslFromRgb(r, g, b);
    var t = tsl[0];
    var s = tsl[1];
    var l = tsl[2];

    document.getElementById('Tdst').value = floatToStrSmart(t);
    document.getElementById('Sdst').value = s;
    document.getElementById('Ldst').value = l;

});

document.getElementById('btnCopyTsl').addEventListener('click', function (e) {
    e.preventDefault();
    document.getElementById('Tsrc').value = document.getElementById('Tdst').value;
    document.getElementById('Ssrc').value = document.getElementById('Sdst').value;
    document.getElementById('Lsrc').value = document.getElementById('Ldst').value;
});


document.getElementById('btnTslToRgb').addEventListener('click', function (e) {
    e.preventDefault();
    var t = parseFloatSmart(document.getElementById('Tsrc').value);
    var s = parseFloat(document.getElementById('Ssrc').value);
    var l = parseFloat(document.getElementById('Lsrc').value);

    document.getElementById('Tsrc').value = floatToStrSmart(t);
    document.getElementById('Ssrc').value = s;
    document.getElementById('Lsrc').value = l;


    var rgb = rgbFromTsl(t, s, l);
    var r = rgb[0];
    var g = rgb[1];
    var b = rgb[2];

    if (document.getElementById('roundRgbDst').checked) {
        document.getElementById('Rdst').value = (r + .1) | 0; //round to int
        document.getElementById('Gdst').value = (g + .1) | 0; //round to int;
        document.getElementById('Bdst').value = (b + .1) | 0; //round to int;;
    }
    else {
        document.getElementById('Rdst').value = r;
        document.getElementById('Gdst').value = g;
        document.getElementById('Bdst').value = b;
    }

});


</script>
</body>
</html>