【问题标题】:How to get XY value from ct in Philips Hue?如何从 Philips Hue 中的 ct 获得 XY 值?
【发布时间】:2021-02-06 10:59:25
【问题描述】:

如何从 ct 中获取 XY 值。

例如: ct = 217,我想得到 x="0.3127569", y= "0.32908"。

我可以使用下面的代码将 XY 值转换为 ct 值。

        float R1 = [hue[0] floatValue];
        float S1 = [hue[1] floatValue];
        
        float result = ((R1-0.332)/(S1-0.1858));
        
        NSString *ctString = [NSString stringWithFormat:@"%f", ((-449*result*result*result)+(3525*result*result)-(6823.3*result)+(5520.33))];
        
        float micro2 = (float) (1 / [ctString floatValue] * 1000000);
        
        NSString *ctValue = [NSString stringWithFormat:@"%f", micro2];
        ctValue = [NSString stringWithFormat:@"%d", [ctValue intValue]];
        if ([ctValue integerValue] < 153) {
            ctValue = [NSString stringWithFormat:@"%d", 153];
        }

现在我想要反向值,即从 ct 到 XY。

【问题讨论】:

  • @skaak:应该有什么帮助?问题是关于颜色的
  • @GiacomoCatenazzi 的问题是关于反转方程......不确定这是否会有所帮助,但数学密度大致相同......
  • 没有逆转的机会。在您的代码中看到[NSString stringWithFormat:@"%d",[[NSString stringWithFormat:@"%f", micro2] intValue]],精度损失
  • 那或多或少是640 320 ^ 3 + 744 = e ^ [ pi . sqrt( 163 ) ]

标签: ios objective-c xcode philips-hue cct


【解决方案1】:

菲利普斯 HUE
2000K 映射到 500 和 6500K 映射到 153
ct 中给出作为色温,但可以认为实际上是Mired
Mired 表示micro reciprocal degree wikipedia

ct 可能被使用,因为它不是 100% Mired。可以肯定 Phillips 使用了很多 CIE 算法所做的查找表,因为在从 153 到 500 的范围内只有 347 个索引。

以下不是解决方案,它只是查找表的简单概念。 正如 McCamy 的 CIE 1931 xy 到 CCT 公式所建议的found here,也可以使用查找表来查找 x 和 y。 可以找到一个表 here 但我不确定这是否是正确的查找表。

提醒所以以下不是解决方案,但要找到反向算法,代码可能会有所帮助。

typedef int Kelvin;
typedef float Mired;

Mired linearMiredByKelvin(Kelvin k) {
    if (k==0) return 0;
    return 1000000.0/k;
}

-(void)mired {
    Mired miredMin = 2000.0/13.0; // 153,84 = reciprocal 6500K
    Mired miredMax = 500.0;       // 500,00 = reciprocal 2000K
    
    Mired lookupMiredByKelvin[6501]; //max 6500 Kelvin + 1 safe index
    //Kelvin lookupKelvinByMired[501]; //max 500 Mired + 1 safe index
    
    // dummy stuff, empty unused table space
    for (Kelvin k = 0; k < 2000; k++) {
        lookupMiredByKelvin[k] = 0;
    }
    //for (Mired m = 0.0; m < 154.0; m++) {
    //    lookupKelvinByMired[(int)m] = 0;
    //}
    
    for (Kelvin k=2000; k<6501; k++) {
        Mired linearMired = linearMiredByKelvin(k);
        float dimm = (linearMired - miredMin) / ( miredMax - miredMin);
        Kelvin ct = (Kelvin)(1000000.0/(dimm*miredMax - dimm*miredMin + miredMin));
        lookupMiredByKelvin[k] = linearMiredByKelvin(ct);
        if (k==2000 || k==2250 || k==2500 || k==2750 ||
            k==3000 || k==3250 || k==3500 || k==3750 ||
            k==4000 || k==4250 || k==4500 || k==4750 ||
            k==5000 || k==5250 || k==5500 || k==5750 ||
            k==6000 || k==6250 || k==6500 || k==6501 )
            fprintf(stderr,"%d %f %f\n",ct, dimm, lookupMiredByKelvin[k]);
    }
}

至少这证明 x 和 y 不会位于一个简单的向量上。

CCT 表示correlated colour temperature,就像问题中的实现一样,可以通过n= (x-0.3320)/(0.1858-y); CCT = 437*n^3 + 3601*n^2 + 6861*n + 5517 计算。 (在 McCamy 之后)

cct=217 超出了上述链接查找表的范围。

遵循colour-science 的这个 git-repo 中的想法 并移植到 C 它可能看起来像..

void CCT_to_xy_CIE_D(float cct) {
    //if (CCT < 4000 || CCT > 25000) fprintf(stderr, "Correlated colour temperature must be in domain, unpredictable results may occur! \n");
    float x =  calculateXviaCCT(cct);
    float y = calculateYviaX(x);
    NSLog(@"cct=%f x%f y%f",cct,x,y);
}

float calculateXviaCCT(float cct) {
    float cct_3 = pow(cct, 3); //(cct*cct*cct);
    float cct_2 = pow(cct, 2); //(cct*cct);
    if (cct<=7000)
    return -4.607 * pow(10, 9) / cct_3 + 2.9678 * pow(10, 6) / cct_2 + 0.09911 * pow(10, 3) / cct + 0.244063;
    return -2.0064 * pow(10, 9) / cct_3 + 1.9018 * pow(10, 6) / cct_2 + 0.24748 * pow(10, 3) / cct + 0.23704;
}
float calculateYviaX(float x) {
    return -3.000 * pow(x, 2) + 2.870 * x - 0.275;
}

CCT_to_xy_CIE_D(6504.38938305); //proof of concept
//cct=6504.389160 x0.312708 y0.329113

CCT_to_xy_CIE_D(217.0); 
//cct=217.000000 x-387.131073 y-450722.750000
// so for sure Phillips hue temperature given in ct between 153-500 is not a good starting point

//but
CCT_to_xy_CIE_D(2000.0);
//cct=2000.000000 x0.459693 y0.410366

这似乎适用于 2000 到 25000 之间的 CCT,但可能令人困惑的是 CCT 在此处以开尔文给出。

【讨论】:

  • 你破解了!很好 - 也在研究它......现在我可以休息了,我从你的三次方程开始......
  • 我想还没有,但该喝咖啡了
【解决方案2】:

编辑

这经历了很多修改和想法。为简单起见,我编辑了大部分内容,然后给你最终结果。

这非常适合您的功能,除了中间的一个区域(温度从 256 到 316)有一点偏差。

你的函数的问题是它有近似无限的解,所以要很好地解决它,你需要更多的约束,但是什么? Ol Sen 的参考资料https://www.waveformlighting.com/tech/calculate-color-temperature-cct-from-cie-1931-xy-coordinates 详细讨论了它,然后提到您希望Duv 为零。它还提供了一种计算 Duv 的方法,因此我将其添加到我的优化器中,瞧!

漂亮而流畅。优化器现在求解 x 和 y,既满足您的功能又最小化 Duv。

为了让它很好地工作,我不得不对 Duv 进行相当大的扩展。该页面提到 Duv 应该非常小,所以我认为这是一件好事。此外,随着温度的升高,缩放应该有助于优化器。

下面的打印从 153 到 500。

#import <Foundation/Foundation.h>

// Function taken from your code
// Simplified a bit
int ctFuncI ( float x, float y )
{
    // float R1 = [hue[0] floatValue];
    // float S1 = [hue[1] floatValue];
    float result = (x-0.332)/(y-0.1858);
    float cubic  = - 449 * result * result * result + 3525 * result * result - 6823.3 * result + 5520.33;
    float micro2 = 1 / cubic * 1000000;
    int ct = ( int )( micro2 + 0.5 );
    
    if ( ct < 153 )
    {
        ct = 153;
    }
    
    return ct;
}

// Need this
// Float version of your code
float ctFuncF ( float x, float y )
{
    // float R1 = [hue[0] floatValue];
    // float S1 = [hue[1] floatValue];
    float result = (x-0.332)/(y-0.1858);
    float cubic  = - 449 * result * result * result + 3525 * result * result - 6823.3 * result + 5520.33;

    return 1000000 / cubic;
}

// We need an additional constraint
// https://www.waveformlighting.com/tech/calculate-duv-from-cie-1931-xy-coordinates
// Given x, y calculate Duv
// We want this to be 0
float duv ( float x, float y )
{
    float f = 1 / ( - 2 * x + 12 * y + 3 );
    float u = 4 * x * f;
    float v = 6 * y * f;

    // I'm typing float but my heart yells double
    float k6 = -0.00616793;
    float k5 =  0.0893944;
    float k4 = -0.5179722;
    float k3 =  1.5317403;
    float k2 = -2.4243787;
    float k1 =  1.925865;
    float k0 = -0.471106;
    
    float du = u - 0.292;
    float dv = v - 0.24;

    float Lfp = sqrt ( du * du + dv * dv );
    float a = acos( du / Lfp );
    float Lbb = k6 * pow ( a, 6 ) + k5 * pow( a, 5 ) + k4 * pow( a, 4 ) + k3 * pow( a, 3 ) + k2 * pow(a,2) + k1 * a + k0;

    return Lfp - Lbb;
}

// Solver!
// Returns iterations
int ctSolve ( int ct, float * x, float * y )
{
    int iter = 0;
    float dx = 0.001;
    float dy = 0.001;

    // Error
    // Note we scale duv a bit
    // Seems the higher the temp, the higher scale we require
    // Also note the jump at 255 ...
    float s = 1000 * ( ct > 255 ? 10 : 1 );
    float d = fabs( ctFuncF ( * x, * y ) - ct ) + s * fabs( duv ( * x, * y ) );
    
    // Approx
    while ( d > 0.5 && iter < 250 )
    {
        iter ++;

        dx *= fabs( ctFuncF ( * x + dx, * y ) - ct ) + s * fabs( duv ( * x + dx, * y ) ) < d ? 1.2 : - 0.5;
        dy *= fabs( ctFuncF ( * x, * y + dy ) - ct ) + s * fabs( duv ( * x, * y + dy ) ) < d ? 1.2 : - 0.5;

        * x += dx;
        * y += dy;

        d = fabs( ctFuncF ( * x, * y ) - ct ) + s * fabs( duv ( * x, * y ) );
    }

    return iter;
}

// Tester
int main(int argc, const char * argv[]) {
    
    @autoreleasepool
    {
        // insert code here...
        NSLog(@"Hello, World!");
        float x, y;
        int sume = 0;
        int sumi = 0;
                
        for ( int ct = 153; ct <= 500; ct ++ )
        {
            // Initial guess
            x = 0.4;
            y = 0.4;

            // Approx
            int iter = ctSolve ( ct, & x, & y );
            
            // CT and error
            int ctEst = ctFuncI ( x, y );
            int e = ct - ctEst;
            
            // Diagnostics
            sume += abs ( e );
            sumi += iter;
            
            // Print out results
            NSLog ( @"want ct = %d x = %f y = %f got ct %d in %d iter error %d", ct, x, y, ctEst, iter, e );
        }
        
        NSLog ( @"Sum of abs errors %d iterations %d", sume, sumi );

    }
    
    return 0;
}

要使用它,请执行以下操作。

// To call it, init x and y to some guess
float x = 0.4;
float y = 0.4;

// Then call solver with your temp
int ct = 217;

ctSolve( ct, & x, & y ); // Note you pass references to x and y

// Done, answer now in x and y

【讨论】:

  • 我喜欢它。问题的问题.. :-D
【解决方案3】:

更紧凑的答案和来回转换的功能..
注意存在舍入问题,因为 McCamy 的 formula 依赖于数学假设。反向计算也是如此。

如果您想找到更多结果,请直接搜索"n= (x-0.3320)/(0.1858-y); CCT = 437*n^3 + 3601*n^2 + 6861*n + 5517",有很多不同的方法可以来回转换。

所以这里是Phillips-Hue @[@x,@y]Phillips-ct
Phillips-ctCCT
CCTx,y

void CCT_to_xy_CIE_D(float cct) {
    //if (CCT < 4000 || CCT > 25000) fprintf(stderr, "Correlated colour temperature must be in domain, unpredictable results may occur! \n");
    float x = calculateXviaCCT(cct);
    float y = calculateYviaX(x);
    fprintf(stderr,"cct=%f x%f y%f",cct,x,y);
}
float calculateXviaCCT(float cct) {
    float cct_3 = pow(cct, 3); //(cct*cct*cct);
    float cct_2 = pow(cct, 2); //(cct*cct);
    if (cct<=7000.0)
    return -4.607 * pow(10, 9) / cct_3 + 2.9678 * pow(10, 6) / cct_2 + 0.09911 * pow(10, 3) / cct + 0.244063;
    return -2.0064 * pow(10, 9) / cct_3 + 1.9018 * pow(10, 6) / cct_2 + 0.24748 * pow(10, 3) / cct + 0.23704;
}
float calculateYviaX(float x) {
    return -3.000 * x*x + 2.870 * x - 0.275;
}
int calculate_PhillipsHueCT_withCCT(float cct) {
    if (cct>6500.0) return 2000.0/13.0;
    if (cct<2000.0) return 500.0;
    //return (float) (1 / cct * 1000000); // same as..
    return 1000000 / cct;
}
float calculate_CCT_withPhillipsHueCT(float ct) {
    if (ct == 0.0) return 0.0;
    return 1000000 / ct;
}
float calculate_CCT_withHueXY(NSArray *hue) {
    float x = [hue[0] floatValue]; //R1
    float y = [hue[1] floatValue]; //S1
    //x = 0.312708; y = 0.329113;
    float n = (x-0.3320)/(0.1858-y);
    float cct = 437.0*n*n*n + 3601.0*n*n + 6861.0*n + 5517.0;
    return cct;
}

// MC Camy formula n=(x-0.3320)/(0.1858-y); cct = 437*n^3 + 3601*n^2 + 6861*n + 5517;
-(void)testPhillipsHueCt_backAndForth {
    NSArray *hue = @[@(0.312708),@(0.329113)];
    
    float cct = calculate_CCT_withHueXY(hue);

    float ct = calculate_PhillipsHueCT_withCCT(cct);
    NSLog(@"ct %f",ct);
    
    CCT_to_xy_CIE_D(cct); // check
    
    CCT_to_xy_CIE_D(6504.38938305); //proof of concept

    CCT_to_xy_CIE_D(2000.0);
    
    CCT_to_xy_CIE_D(calculate_CCT_withPhillipsHueCT(217.0));
}

【讨论】:

  • 很好 - 2000 年左右的误差很大......这里的范围是多少? 2000 - 6500?
  • 是的.. 2000 - 6500,实际上是 2000-25000。低于其非常“胀气”,即使仍然有点合乎逻辑。
  • 我正在考虑将其转换为 153 - 500 范围。如果我从 2000 - 6500 运行它并使用 OP 的公式,我会得到 154 - 371,在 6500 一侧有 154,所以即使我做 2000 - 25000,它也会保持在这个范围内。总之……你说胀气……
  • 乐趣在于 CCT 和 CT 之间返回相同的公式 1000000/(ct 或 cct),请参阅 calculate_PhillipsHueCT_withCCT()calculate_CCT_withPhillipsHueCT()
  • 是的,注意到...我从 2000 到 6500 运行 CT 以获得 x 和 y,然后将其插入 OP 并从 154 到 371 获得 OP CT 只是 FWIW ...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-26
  • 1970-01-01
  • 2015-07-01
  • 2015-06-05
  • 1970-01-01
  • 2013-08-18
  • 1970-01-01
相关资源
最近更新 更多