Star BV颜色指数与明显的RGB颜色

mel*_*iny 22 rgb colors astronomy dart

我正在尝试将明星的BV颜色索引转换为明显的RGB颜色.除了查找表格和颜色渐变之外,似乎没有众所周知的算法可以做到这一点.

什么是BV颜色指数?

这是天文学家为一颗恒星指定其明显颜色的数字.热星(低BV)是蓝色/紫色,冷色星(高BV)是红色的,其间有白色/橙色星.

初始算法

BV到开尔文

在此输入图像描述

var t = 4600 * ((1 / ((0.92 * bv) + 1.7)) +(1 / ((0.92 * bv) + 0.62)) );
Run Code Online (Sandbox Code Playgroud)

开尔文到xyY

如果将星型建模为黑体,则可以使用普朗克轨迹的数值近似来计算xy坐标(CIE色度)

在此输入图像描述

在此输入图像描述

在此输入图像描述

// t to xyY
var x, y = 0;

if (t>=1667 && t<=4000) {
  x = ((-0.2661239 * Math.pow(10,9)) / Math.pow(t,3)) + ((-0.2343580 * Math.pow(10,6)) / Math.pow(t,2)) + ((0.8776956 * Math.pow(10,3)) / t) + 0.179910;
} else if (t > 4000 && t <= 25000) {
  x = ((-3.0258469 * Math.pow(10,9)) / Math.pow(t,3)) + ((2.1070379 * Math.pow(10,6)) / Math.pow(t,2)) + ((0.2226347 * Math.pow(10,3)) / t) + 0.240390;
}

if (t >= 1667 && t <= 2222) {
  y = -1.1063814 * Math.pow(x,3) - 1.34811020 * Math.pow(x,2) + 2.18555832 * x - 0.20219683;
} else if (t > 2222 && t <= 4000) {
  y = -0.9549476 * Math.pow(x,3) - 1.37418593 * Math.pow(x,2) + 2.09137015 * x - 0.16748867;
} else if (t > 4000 && t <= 25000) {
  y = 3.0817580 * Math.pow(x,3) - 5.87338670 * Math.pow(x,2) + 3.75112997 * x - 0.37001483;
}
Run Code Online (Sandbox Code Playgroud)

xyY到XYZ(Y = 1)

在此输入图像描述

在此输入图像描述

// xyY to XYZ, Y = 1
var Y = (y == 0)? 0 : 1;
var X = (y == 0)? 0 : (x * Y) / y;
var Z = (y == 0)? 0 : ((1 - x - y) * Y) / y;
Run Code Online (Sandbox Code Playgroud)

XYZ到RGB

在此输入图像描述

var r = 0.41847 * X - 0.15866 * Y - 0.082835 * Z;
var g = -0.091169 * X + 0.25243 * Y + 0.015708 * Z;
var b = 0.00092090 * X - 0.0025498 * Y + 0.17860 * Z;
Run Code Online (Sandbox Code Playgroud)

我使用BV颜色索引运行此算法:1.2,1.0,0.59,0.0,-0.29.这就是我得到的输出.

在此输入图像描述

为什么我得到这个奇怪的输出?热的恒星是蓝色的,但冷的恒星是褐色的,似乎没有白色/橙色的中间恒星.

更新

Ozan的评论之后,似乎我使用了错误的矩阵将XYZ转换为RGB.由于sRGB是网络上的默认色彩空间(或者是?),我现在使用正确的矩阵,然后使用伽马校正功能(a = 0.055).

在此输入图像描述

在此输入图像描述

我现在得到这个漂亮的颜色渐变,

在此输入图像描述

但四肢仍然没有红/紫.

演示

现在你可以玩一个小提琴.

更新2

如果使用0.5的伽玛并将BV颜色指数的范围扩展到4.7到-0.5,我会在一个极端变红但仍然没有紫色.这是更新的小提琴.

在此输入图像描述

Spe*_*tre 12

我使用tabled插值代替.几年前我在某处发现了这张桌子:

     type     r   g   b    rrggbb        B-V

     O5(V)   155 176 255  #9bb0ff       -0.32 blue
     O6(V)   162 184 255  #a2b8ff
     O7(V)   157 177 255  #9db1ff
     O8(V)   157 177 255  #9db1ff
     O9(V)   154 178 255  #9ab2ff
   O9.5(V)   164 186 255  #a4baff
     B0(V)   156 178 255  #9cb2ff
   B0.5(V)   167 188 255  #a7bcff
     B1(V)   160 182 255  #a0b6ff
     B2(V)   160 180 255  #a0b4ff
     B3(V)   165 185 255  #a5b9ff
     B4(V)   164 184 255  #a4b8ff
     B5(V)   170 191 255  #aabfff
     B6(V)   172 189 255  #acbdff
     B7(V)   173 191 255  #adbfff
     B8(V)   177 195 255  #b1c3ff
     B9(V)   181 198 255  #b5c6ff
     A0(V)   185 201 255  #b9c9ff       0.00 White
     A1(V)   181 199 255  #b5c7ff
     A2(V)   187 203 255  #bbcbff
     A3(V)   191 207 255  #bfcfff
     A5(V)   202 215 255  #cad7ff
     A6(V)   199 212 255  #c7d4ff
     A7(V)   200 213 255  #c8d5ff
     A8(V)   213 222 255  #d5deff
     A9(V)   219 224 255  #dbe0ff
     F0(V)   224 229 255  #e0e5ff       0.31 yellowish
     F2(V)   236 239 255  #ecefff
     F4(V)   224 226 255  #e0e2ff
     F5(V)   248 247 255  #f8f7ff
     F6(V)   244 241 255  #f4f1ff
     F7(V)   246 243 255  #f6f3ff       0.50
     F8(V)   255 247 252  #fff7fc
     F9(V)   255 247 252  #fff7fc
     G0(V)   255 248 252  #fff8fc       0.59  Yellow
     G1(V)   255 247 248  #fff7f8
     G2(V)   255 245 242  #fff5f2
     G4(V)   255 241 229  #fff1e5
     G5(V)   255 244 234  #fff4ea
     G6(V)   255 244 235  #fff4eb
     G7(V)   255 244 235  #fff4eb
     G8(V)   255 237 222  #ffedde
     G9(V)   255 239 221  #ffefdd
     K0(V)   255 238 221  #ffeedd       0.82 Orange
     K1(V)   255 224 188  #ffe0bc
     K2(V)   255 227 196  #ffe3c4
     K3(V)   255 222 195  #ffdec3
     K4(V)   255 216 181  #ffd8b5
     K5(V)   255 210 161  #ffd2a1
     K7(V)   255 199 142  #ffc78e
     K8(V)   255 209 174  #ffd1ae
     M0(V)   255 195 139  #ffc38b       1.41 red
     M1(V)   255 204 142  #ffcc8e
     M2(V)   255 196 131  #ffc483
     M3(V)   255 206 129  #ffce81
     M4(V)   255 201 127  #ffc97f
     M5(V)   255 204 111  #ffcc6f
     M6(V)   255 195 112  #ffc370
     M8(V)   255 198 109  #ffc66d       2.00
Run Code Online (Sandbox Code Playgroud)
  1. 只是在使用前插入缺失的BV索引(线性或更好)
  2. 然后使用线性插值得到RGB = f(BV);
  3. 找到表中最近的两行并在它们之间进行插值......

[编辑]嘿,恰巧碰到了这个(我之前提到的原始信息)

[edit2]这是我的近似,没有任何XYZ的东西

BV到RGB

因此BV指数来自 < -0.4 , 2.0 >

这是我的(C++)转换代码:

//---------------------------------------------------------------------------
void bv2rgb(double &r,double &g,double &b,double bv)    // RGB <0,1> <- BV <-0.4,+2.0> [-]
    {
    double t;  r=0.0; g=0.0; b=0.0; if (bv<-0.4) bv=-0.4; if (bv> 2.0) bv= 2.0;
         if ((bv>=-0.40)&&(bv<0.00)) { t=(bv+0.40)/(0.00+0.40); r=0.61+(0.11*t)+(0.1*t*t); }
    else if ((bv>= 0.00)&&(bv<0.40)) { t=(bv-0.00)/(0.40-0.00); r=0.83+(0.17*t)          ; }
    else if ((bv>= 0.40)&&(bv<2.10)) { t=(bv-0.40)/(2.10-0.40); r=1.00                   ; }
         if ((bv>=-0.40)&&(bv<0.00)) { t=(bv+0.40)/(0.00+0.40); g=0.70+(0.07*t)+(0.1*t*t); }
    else if ((bv>= 0.00)&&(bv<0.40)) { t=(bv-0.00)/(0.40-0.00); g=0.87+(0.11*t)          ; }
    else if ((bv>= 0.40)&&(bv<1.60)) { t=(bv-0.40)/(1.60-0.40); g=0.98-(0.16*t)          ; }
    else if ((bv>= 1.60)&&(bv<2.00)) { t=(bv-1.60)/(2.00-1.60); g=0.82         -(0.5*t*t); }
         if ((bv>=-0.40)&&(bv<0.40)) { t=(bv+0.40)/(0.40+0.40); b=1.00                   ; }
    else if ((bv>= 0.40)&&(bv<1.50)) { t=(bv-0.40)/(1.50-0.40); b=1.00-(0.47*t)+(0.1*t*t); }
    else if ((bv>= 1.50)&&(bv<1.94)) { t=(bv-1.50)/(1.94-1.50); b=0.63         -(0.6*t*t); }
    }
//---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

[笔记]

该BV颜色是​​定义温度照明的黑体,因此这表示从空间相对于恒星观察的星形颜色.对于视觉上正确的颜色,你必须添加我们的大气散射效果和多普勒效应快速割草明星!例如我们的太阳是'白色',但在光线散射后,颜色从红色(近地平线)到黄色(近天底...中午)变化

如果您想在视觉上纠正颜色,这些QA可能会有所帮助:


Doc*_*ard 7

你问了一个算法,你会得到一个算法.

当我使用Pyglet和MongoDB 从Python3.5中的HYG数据库渲染数据时,我研究了这个主题.我很满意我的星星在我的星图中的样子.颜色可以在这个答案的底部找到.

1.颜色指数(BV)到温度(K)

这是我在HYG数据库的BV(ci)数据上使用的函数.在这个例子中,ci是我正在运行的列表中的BV值.

    temp = 4600 * (1 / (0.92 * ci + 1.7) + 1 / (0.92 * ci + 0.62))
Run Code Online (Sandbox Code Playgroud)

2.拿一张大桌子.

我拿了这个,我建议你也这样做.选择温度列和RGB或rgb值列作为参考

3.预处理数据.

从rgb表数据中,我生成了三个有序列表(n = 391)(我的方法:使用电子表格软件进行清理和选择以及一次能够有数百万个游标的文本编辑器,然后通过mongoDB导入生成的逗号分隔文件)所以我可以通过pymongo包装器轻松处理python中的值列表,而不会在脚本文件中产生太多混乱).我将要介绍的方法的好处是,您可以从其他可能使用CMYK或HSV的表中提取颜色数据并进行相应调整.你甚至可以交叉引用.但是,您应该从我建议的(s)RGB表中得到这样的列表;

    reds = [255, 255, ... , 155, 155]
    greens = [56, 71, ..., 188,188]
    blues = [0, 0, ..., 255, 255]

    """ this temps list is also (n=391) and corresponds to the table values."""
    temps = []
    for i in range(1000,40100,100):
        temps.append(i)
Run Code Online (Sandbox Code Playgroud)

在此之后,我对这些列表应用了一些高斯平滑(它有助于获得更好的多项式,因为它消除了一些波动),之后我将polyfit()方法(多项式回归)从numpy包应用到温度值相对于所述R,G和B值:

colors = [reds,greens,blues]

""" you can tweak the degree value to see if you can get better coeffs. """
def smoothListGaussian2(myarray, degree=3):
    myarray = np.pad(myarray, (degree-1,degree-1), mode='edge')
    window=degree*2-1
    weight=np.arange(-degree+1, degree)/window
    weight = np.exp(-(16*weight**2))
    weight /= sum(weight)
    smoothed = np.convolve(myarray, weight, mode='valid')
    return smoothed

i=0

for color in colors:

    color = smoothListGaussian2(color)
    x = np.array(temps)
    y = np.array(color)

    names = ["reds","greens","blues"]
    """ raise/lower the k value (third one) in c """
    z = np.polyfit(x, y, 20)
    f = np.poly1d(z)
    #plt.plot(x,f(x),str(names[i][0]+"-"))
    print("%sPoly = " % names[i], z)

    i += 1
plt.show()
Run Code Online (Sandbox Code Playgroud)

这给出了(n)系数(a)的多项式形式:

在此输入图像描述.

现在来想想,你可以使用polyfit来提出将CI直接转换为RGB的系数......并跳过CI到温度转换步骤,但是首先转换为temp,温度和所选温度之间的关系色彩空间更清晰.

4.实际算法:将温度值插入RGB多项式

正如我之前所说,你可以使用其他光谱数据和其他颜色空间来拟合多项式曲线,这一步仍然是相同的(略有修改)

无论如何,这里是我使用的完整的简单代码(同样,这是k = 20个多项式):

import numpy as np

redco = [ 1.62098281e-82, -5.03110845e-77, 6.66758278e-72, -4.71441850e-67, 1.66429493e-62, -1.50701672e-59, -2.42533006e-53, 8.42586475e-49, 7.94816523e-45, -1.68655179e-39, 7.25404556e-35, -1.85559350e-30, 3.23793430e-26, -4.00670131e-22, 3.53445102e-18, -2.19200432e-14, 9.27939743e-11, -2.56131914e-07,  4.29917840e-04, -3.88866019e-01, 3.97307766e+02]
greenco = [ 1.21775217e-82, -3.79265302e-77, 5.04300808e-72, -3.57741292e-67, 1.26763387e-62, -1.28724846e-59, -1.84618419e-53, 6.43113038e-49, 6.05135293e-45, -1.28642374e-39, 5.52273817e-35, -1.40682723e-30, 2.43659251e-26, -2.97762151e-22, 2.57295370e-18, -1.54137817e-14, 6.14141996e-11, -1.50922703e-07,  1.90667190e-04, -1.23973583e-02,-1.33464366e+01]
blueco = [ 2.17374683e-82, -6.82574350e-77, 9.17262316e-72, -6.60390151e-67, 2.40324203e-62, -5.77694976e-59, -3.42234361e-53, 1.26662864e-48, 8.75794575e-45, -2.45089758e-39, 1.10698770e-34, -2.95752654e-30, 5.41656027e-26, -7.10396545e-22, 6.74083578e-18, -4.59335728e-14, 2.20051751e-10, -7.14068799e-07,  1.46622559e-03, -1.60740964e+00, 6.85200095e+02]

redco = np.poly1d(redco)
greenco = np.poly1d(greenco)
blueco = np.poly1d(blueco)

def temp2rgb(temp):

    red = redco(temp)
    green = greenco(temp)
    blue = blueco(temp)

    if red > 255:
        red = 255
    elif red < 0:
        red = 0
    if green > 255:
        green = 255
    elif green < 0:
        green = 0
    if blue > 255:
        blue = 255
    elif blue < 0:
        blue = 0

    color = (int(red),
             int(green),
             int(blue))
    print(color)
    return color
Run Code Online (Sandbox Code Playgroud)

哦,还有一些笔记和图像......

我的多项式的OBAFGKM黑体温标度:

在此输入图像描述

RGB [0-255]超过温度[0-40000K]的图,

  • +:表格数据
  • 曲线:多项式拟合 在此输入图像描述 放大最低保真度值: 在此输入图像描述

这是紫色的

正如你所看到的,有一些偏差,但肉眼难以察觉,如果你真的想改进它(我没有),你还有其他一些选择:

  1. 划分绿色值最高的列表,看看是否为列表的新左侧和右侧部分获得了更好的多项式.有点像这样:

纠正措施.

  1. 为此最低保真度窗口中的值编写例外规则(可能是简单的k = 2或k = 3 poly).
  2. 在polyfit()之前尝试其他平滑算法.
  3. 尝试其他来源或色彩空间.

我也对我的多项式的整体性能感到满意.当我加载我的星图的~120000个星形物体时,每个物体至少有18个彩色顶点,它只需要几秒钟,这让我大吃一惊.但是,还有改进的余地.为了获得更逼真的视图(而不仅仅是运行黑体光辐射),我可以添加引力透镜,大气效果,相对论多普勒等等......

哦,和紫色,如同承诺的那样.

其他一些有用的链接:


pad*_*dyg 5

以防万一其他人需要将方便的C ++ @Spektre转换为python。我已经删除了一些重复项(编译器无疑会修复)和g when bv>=2.0和b when 的不连续性1.94<bv<1.9509

def bv2rgb(bv):
  if bv < -0.4: bv = -0.4
  if bv > 2.0: bv = 2.0
  if bv >= -0.40 and bv < 0.00:
    t = (bv + 0.40) / (0.00 + 0.40)
    r = 0.61 + 0.11 * t + 0.1 * t * t
    g = 0.70 + 0.07 * t + 0.1 * t * t
    b = 1.0
  elif bv >= 0.00 and bv < 0.40:
    t = (bv - 0.00) / (0.40 - 0.00)
    r = 0.83 + (0.17 * t)
    g = 0.87 + (0.11 * t)
    b = 1.0
  elif bv >= 0.40 and bv < 1.60:
    t = (bv - 0.40) / (1.60 - 0.40)
    r = 1.0
    g = 0.98 - 0.16 * t
  else:
    t = (bv - 1.60) / (2.00 - 1.60)
    r = 1.0
    g = 0.82 - 0.5 * t * t
  if bv >= 0.40 and bv < 1.50:
    t = (bv - 0.40) / (1.50 - 0.40)
    b = 1.00 - 0.47 * t + 0.1 * t * t
  elif bv >= 1.50 and bv < 1.951:
    t = (bv - 1.50) / (1.94 - 1.50)
    b = 0.63 - 0.6 * t * t
  else:
    b = 0.0
  return (r, g, b)
Run Code Online (Sandbox Code Playgroud)


Aym*_*icG 5

作为对 @paddyg 代码的修正,它对我不起作用(特别是对于 bv < 0.4 的颜色):这里是 @Spektre 的 C++ 代码的完全相同版本,在 Python 中:

def bv2rgb(bv):
    if bv < -0.40: bv = -0.40
    if bv > 2.00: bv = 2.00

    r = 0.0
    g = 0.0
    b = 0.0

    if  -0.40 <= bv<0.00:
        t=(bv+0.40)/(0.00+0.40)
        r=0.61+(0.11*t)+(0.1*t*t)
    elif 0.00 <= bv<0.40:
        t=(bv-0.00)/(0.40-0.00)
        r=0.83+(0.17*t)
    elif 0.40 <= bv<2.10:
        t=(bv-0.40)/(2.10-0.40)
        r=1.00
    if  -0.40 <= bv<0.00:
        t=(bv+0.40)/(0.00+0.40)
        g=0.70+(0.07*t)+(0.1*t*t)
    elif 0.00 <= bv<0.40:
        t=(bv-0.00)/(0.40-0.00)
        g=0.87+(0.11*t)
    elif 0.40 <= bv<1.60:
        t=(bv-0.40)/(1.60-0.40)
        g=0.98-(0.16*t)
    elif 1.60 <= bv<2.00:
        t=(bv-1.60)/(2.00-1.60)
        g=0.82-(0.5*t*t)
    if  -0.40 <= bv<0.40:
        t=(bv+0.40)/(0.40+0.40)
        b=1.00
    elif 0.40 <= bv<1.50:
        t=(bv-0.40)/(1.50-0.40)
        b=1.00-(0.47*t)+(0.1*t*t)
    elif 1.50 <= bv<1.94:
        t=(bv-1.50)/(1.94-1.50)
        b=0.63-(0.6*t*t)

    return (r, g, b)
Run Code Online (Sandbox Code Playgroud)