图像中照明源(星星)的颜色管理

问题描述 投票:0回答:1

我正在处理星空图像。对于给定的星星,我想在给定的图像工作色彩空间中获得代表性的 RGB 颜色,这可能不是 sRGB。 (如果我只需要担心 sRGB,问题就已经解决了。)我可以在恒星目录中查找恒星的详细信息并获取恒星的 B-V(这是一种光度测量,重要的是,有是根据恒星的 B-V 计算恒星有效温度的公式)。我可以将有效温度转换为一组 CIE1931 xy 坐标,然后将其转换为 XYZ(同样,如何做到这一点不是问题)。我正在使用 lcms2 来执行颜色空间转换。

为了提供恒星颜色的合理自然的表示,太阳被视为白色。这实际上是 D58 的白点,而图像的工作色彩空间可能是 sRGB 或 Rec2020(白点 D65)或 Adobe RGB(白点 D50)或其他可能的颜色空间。

我不完全确定如何使颜色看起来正确。

如果我使用相对色度意图将 XYZ 转换为工作 ICC 配置文件,则白点看起来错误,我认为这是因为它使工作配置文件的白点充当数据的白点,而我希望它们拥有自己的白点白点,与我放置它们的图像不同。

相反,我是否应该首先将 XYZ 转换为具有 D58 白点的中间 RGB ICC 配置文件(有效地将其用作源配置文件),然后使用绝对比色意图将该 RGB 值转换为图像工作配置文件?

light color-management
1个回答
0
投票

好的,当我开始工作时,我会自己回答这个问题。

第 1 步:从恒星表(例如 Simbad)中查找每颗恒星的 B 星等和 V 星等。我将跳过这一点,因为它不是问题的核心,并且目录已经合理记录了查询 API。

第2步:计算恒星的B-V:

results[n].BV = star[i].bmag - star[i].vmag;

第3步:将B-V转换为温度。最简单的方法是使用以下函数实现的公式(注释中引用):

// Reference: https://en.wikipedia.org/wiki/Color_index and https://arxiv.org/abs/1201.1809 (Ballesteros, F. J., 2012)
// Uses Ballesteros' formula based on considering stars as black bodies
double bvToT(double bv) {
    double t = 4600. * ((1. / ((0.92 * bv) + 1.7)) + (1. / ((0.92 * bv) + 0.62)));
    return t;
}

您可以尝试查找每颗恒星的光谱类型,并对每种光谱类型使用单独的 bvToT 计算,但上述方法通常已经足够了,特别是当您要统计大量恒星时。

第 4 步:将温度转换为 CIE xy: 您可以在此处使用多种方法。第一个是使用 Mitchell Charity 的 Black 体温到 CIExy 转换表。这些版本有使用带有 Judd-Vos 校正的 1931 2 度 CMF 和 1964 10 度 CMF 的版本,并且它们可用于 D65 和 D58 白色参考。查找表和插值的实现很简单。这些表声称有效期为 1,000K 到 40,000K。其次,您可以使用 Kim 等人的三次样条方法,按照下面的函数(评论中的参考):

// Returns a valid xyY for 1667K <= t <= 25000K, otherwise xyY = { 0.0 }
// Uses Kim et al's cubic spline Planckian locus (https://en.wikipedia.org/wiki/Planckian_locus)
static void temp_to_xyY(cmsCIExyY *xyY, cmsFloat64Number t) {
    // Calculate x
    if (t < 1667.0)
        xyY->x = 0.0;
    else if (t < 4000.0)
        xyY->x = (-0.2661239e9 / (t * t * t)) - (0.2343589e6 / (t * t)) +( 0.8776956e3 / t) + 0.179910;
    else if (t < 25000.0)
        xyY->x = (-3.0258469e9 / (t * t * t)) + (2.1070379e6 / (t * t)) + (0.2226347e3 / t) + 0.240390;
    else
        xyY->x = 0.0;

    cmsFloat64Number x = xyY->x;
    // Calculate y
    if (t < 1667)
        xyY->y = 0.0;
    else if (t < 2222.0)
        xyY->y = (-1.1063814 * x * x * x) - (1.34811020 * x * x) + (2.18555832 * x) - 0.20219683;
    else if (t < 4000.0)
        xyY->y = (-0.9549476 * x * x * x) - (1.37418593 * x * x) + (2.09137015 * x) - 0.16748867;
    else if (t < 25000.0)
        xyY->y = (3.0817580 * x * x * x) - (5.87338670 * x * x) + (3.75112997 * x) - 0.37001483;
    else
        xyY->y = 0.0;

    if (!(xyY->x == 0.0 && xyY->y == 0.0))
        xyY->Y = 1.0;
    else
        xyY->Y = 0.0;
}

有效温度范围没有那么宽,但它避免了存储 LUT。 由于接下来会发生什么,我现在开始使用 lcms2 API 来处理数据类型。 最后,BQ Octantis在Cloudy Nights天文论坛上提出了一个公式。评论中提供的参考:

// Returns a valid xyY for 2000K <= t, otherwise xyY = { 0.0 }
// Uses BQ Octantis's 6th order best fit for D65 values down to 2000K
// (https://www.cloudynights.com/topic/849382-generating-a-planckian-ccm/)
static void BQ_temp_to_xyY(cmsCIExyY *xyY, cmsFloat64Number t) {
    if (t < 2000.0) {
        xyY->x = xyY->y = xyY->Y = 0.0;
    } else {
        xyY->x = -1.3737e-25 * pow(t,6.0) + 3.0985e-22 * pow(t, 5.0) + 1.5842e-16 * pow(t, 4.0)
                - 4.0138e-12 * pow(t, 3.0) + 4.3777e-08 * pow(t, 2.0) - 2.4363e-04 * t + 8.7309e-01;
        cmsFloat64Number x = xyY->x;
        xyY->y =  2.5110e+01 * pow(x, 5.0) - 5.9883e+01 * pow(x, 4.0) + 5.5545e+01 * pow(x, 3.0)
                - 2.7667e+01 * pow(x, 2.0) + 8.1167e+00 * x - 7.0462e-01;
        xyY->Y = 1.0;
    }
}

这是一个单一的六阶样条,但需要昂贵的函数调用 pow() 来计算。

第 5 步:将 xyY 转换为 XYZ: 使用 lcms2 这很简单:

cmsxyY2XYZ(&XYZ, &xyY);

其中 xyY 是您在步骤 4 中计算的黑体颜色。

第6步:调整色度:普朗克轨迹公式基于D65,而XYZ配置文件中内置的lcms2基于D50,因此我们进行色度调整:

const cmsCIEXYZ D65 = {0.95045471, 1.0, 1.08905029};
const cmsCIEXYZ D50 = {0.964199999, 1.000000000, 0.824899998};
cmsCIXYZ XYZ, XYZ_adapted;
cmsAdaptToIlluminant(&XYZ_adapted, &D65, &D50, &XYZ);

第七步:转换为工作色彩空间RGB: 最后,创建一个从 XYZ 到您选择的工作色彩空间的 cmsHTRANSFORM。假设您的工作配置文件是 cmsHPROFILE 配置文件...

float xyz[3], rgb[3] = { 0.f };
xyz[0] = (float) XYZ_adapted.X;
xyz[1] = (float) XYZ_adapted.Y;
xyz[2] = (float) XYZ_adapted.Z;

cmsHPROFILE xyzprofile = cmsCreateXYZProfile();
cmsHTRANSFORM transform = cmsCreateTransformTHR(com.icc.context_single,
            xyzprofile,
            TYPE_XYZ_FLT,
            profile,
            TYPE_RGB_FLT,
            INTENT_RELATIVE_COLORIMETRIC,
            cmsFLAGS_NONEGATIVES);
cmsCloseProfile(profile);
cmsCloseProfile(xyzprofile);
cmsDoTransform(transform, &xyz, &rgb, 1);
cmsFloat64Number maxval = max(max(rgb[0], rgb[1]), rgb[2]);
rgb[0] /= maxval;
rgb[1] /= maxval;
rgb[2] /= maxval;

替代方案: 还有直接从 B-V 值转换为 sRGB R、G 和 B 值的简单查找表。如果您只关心 sRGB 那么它们绝对没问题,但是虽然上面概述的方法稍微复杂一些,但它公开了所有工作原理,并且还允许您生成 R、G 和 B 值(甚至 CMYK 值,我猜测)这对于您正在使用的任何色彩空间都是正确的。

© www.soinside.com 2019 - 2024. All rights reserved.