我试图将恒星的B-V color index转换为明显的RGB颜色。除了查找表格和颜色渐变之外,似乎没有众所周知的算法可以做到这一点。
这是天文学家为一颗恒星指定其明显颜色的数字。热星(低B-V)为蓝色/紫色,冷色星(高B-V)为红色,其间为白色/橙色星。
var t = 4600 * ((1 / ((0.92 * bv) + 1.7)) +(1 / ((0.92 * bv) + 0.62)) );
如果您将恒星建模为黑体,则可以使用Planckian locus的数值近似来计算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;
}
// 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;
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;
我使用B-V颜色索引运行此算法:1.2,1.0,0.59,0.0,-0.29。这就是我得到的输出。
为什么我得到这个奇怪的输出?热的恒星是蓝色的,但冷的恒星是褐色的,似乎没有白色/橙色的中间恒星。
继Ozan的评论之后,似乎我使用了错误的矩阵将XYZ转换为RGB。由于sRGB是网络上的默认色彩空间(或者是?),我现在使用正确的矩阵,然后使用伽马校正功能(a = 0.055
)。
我现在得到这个漂亮的颜色渐变,
但四肢仍然没有红/紫。
现在还有一个fiddle你可以玩。
如果使用0.5的伽玛并将B-V颜色指数的范围扩展到4.7到-0.5,我会在一个极端变红但仍然没有紫色。这是更新的fiddle。
我使用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
[编辑]嘿恰巧碰到this(我之前提到的原始信息)
[edit2]这是我的近似,没有任何XYZ的东西
所以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); }
}
//---------------------------------------------------------------------------
[笔记]
该BV颜色是定义温度照明的黑体,因此这表示从空间相对于恒星观察的星形颜色。对于视觉上正确的颜色,你必须添加我们的大气散射效果和多普勒效应快速割草明星!例如我们的太阳是'白色',但在光线散射后,颜色从红色(近地平线)到黄色(近天底...中午)变化
如果您想要在视觉上纠正这些QA可能有用的颜色:
当我使用Pyglet和MongoDB从Python3.5中的the HYG database渲染数据时,我研究了这个主题。我很满意我的星星在我的星图中的样子。颜色可以在这个答案的底部找到。
这是我在the HYG database的B-V(ci)数据中使用的函数。在这个例子中,ci是我正在运行的列表中的B-V值。
temp = 4600 * (1 / (0.92 * ci + 1.7) + 1 / (0.92 * ci + 0.62))
我拿了this one,我建议你也这样做。选择温度列和RGB或rgb值列作为参考
从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)
在此之后,我对这些列表应用了一些高斯平滑(它有助于获得更好的多项式,因为它消除了一些波动),之后我将nazy包中的polyfit()方法(多项式回归)应用于温度值。关于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()
这给出了(n)系数(a)的多项式形式:
现在来想想,你可以使用polyfit来提出将CI直接转换为RGB的系数......并跳过CI到温度转换步骤,但是首先转换为temp,温度和所选温度之间的关系色彩空间更清晰。
正如我之前所说,你可以使用其他光谱数据和其他颜色空间来拟合多项式曲线,这一步仍然是相同的(略有修改)
无论如何,这里是我使用的完整的简单代码(同样,这是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
我的多项式的OBAFGKM黑体温标度:
RGB [0-255]超过温度[0-40000K]的图,
正如你所看到的,有一些偏差,但肉眼难以察觉,如果你真的想改进它(我没有),你还有其他一些选择:
我也对我的多项式的整体性能感到满意。当我加载我的星图的~120000个星形物体时,每个物体至少有18个彩色顶点,它只需要几秒钟,这让我大吃一惊。但是,还有改进的余地。为了获得更逼真的视图(而不仅仅是运行黑体光辐射),我可以添加引力透镜,大气效果,相对论多普勒等等......
哦,和承诺的PURPLE一样。
其他一些有用的链接:
以防万一其他人需要将@Spektre的方便C ++转换为python。我已经采取了一些复制(编译器毫无疑问已经修复)和当bv>=2.0
和b时qzxswpoi的g的不连续性
1.94<bv<1.9509
作为对@paddyg的代码的修正,这对我不起作用(特别是对于bv <0.4的颜色):这里是@Spektre的C ++代码完全相同的版本,在Python中:
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)
为什么没有紫罗兰色或深蓝色?无限的色温,在我们的大气层变得不那么偏蓝之前,有1931 CIE坐标X = .240,y = .234。
无限色温下的黑体光谱具有光谱功率分布,每单位波长带宽的功率与波长成反比,与第四功率成反比。在700nm处,这比在400nm处高10.7%。
在回答为什么没有紫罗兰的问题? :我认为答案是明星不是那种颜色。或者更确切地说,当我们拍摄它们时,它们不会呈现那种颜色。在这个螺纹上产生的各种温度/ B-V值的颜色对我来说似乎非常准确。拍下我在Cygnus拍摄的Albireo照片: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)
Albireo A(左)是K型恒星,B-V为1.074,Alberio B(右)是B型恒星,B-V为-0.06。看看上面图表中的那些B-V值的颜色,我会说这与图片有很强的相关性。此外,不要忘记即使对于非常热的恒星,在较长波长处仍会有一些输出,这将导致“蓝色”饱和度降低。黑体辐射是广谱的。
@ Spektre在Swift 3.0中的回答:
https://www.flickr.com/photos/30974264@N02/6939409750/in/photolist-bB54th-bzdhKG
同样基于列表(private func bv2ToRGB(for bv: CGFloat, logging: Bool = false) -> Color {
var bv = bv
var t: CGFloat = 0
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
if bv < -0.4 { bv = -0.4}
if bv > 2.0 { bv = 2.0}
switch bv {
case -0.4 ... 0.0:
t = (bv+0.40)/(0.00+0.40)
r = 0.61+(0.11*t)+(0.1*t*t)
case 0.0 ... 0.4:
t = (bv-0.00)/(0.40-0.00)
r = 0.83+(0.17*t)
case 0.4 ... 2.1:
t = (bv-0.40)/(2.10-0.40)
r = 1.00
default: break
}
switch bv {
case -0.4 ... 0.0:
t = (bv+0.40)/(0.00+0.40)
g = 0.70 + (0.07*t)+(0.1*t*t)
case 0.0 ... 0.4:
t = (bv-0.00)/(0.40-0.00)
g = 0.87 + (0.11*t)
case 0.4 ... 1.6:
t = (bv-0.40)/(1.60-0.40)
g = 0.98 - (0.16*t)
case 1.6 ... 2.0:
t = (bv-1.60)/(2.00-1.60)
g = 0.82 - (0.5*t*t)
default: break
}
switch bv {
case -0.4 ... 0.4:
t = (bv+0.40)/(0.40+0.40)
b = 1.0
case 0.4 ... 1.5:
t = (bv-0.40)/(1.50-0.40)
b = 1.00 - (0.47*t)+(0.1*t*t)
case 1.5 ... 1.94:
t = (bv-1.50)/(1.94-1.50)
b = 0.63 - (0.6*t*t)
default: break
}
#if os(OSX)
return NSColor(calibratedRed: r, green: g, blue: b, alpha: 1.0)
#else
return UIColor(red: r, green: g, blue: b, alpha: 1.0)
#endif
}
),以下函数使用kotlin根据2deg标度获得温度的颜色:
http://www.vendian.org/mncharity/dir3/blackbody/UnstableURLs/bbr_color.html