我想在python
' matplotlib
库中制作一个带有渐变和吸收线的恒星光谱。
作为输入,我有一个.dat
文件,其中给定波长的相对强度为[wavelenght in angstrom] [intensity]
。
使用给定数据创建此类渐变的好方法是什么? matplotlib
是否提供与可见光相对应的渐变?如何转换Angstrom中给出的wavelenght以适应给定的渐变。
非常感谢详细的回复!
UPD#1
好的,我找到了几种将波长转换为RGB(here和here)的方法。但我仍然不知道如何应用强度(可能是一些播放alpha通道?),更重要的是,如何使用自定义配色方案构建渐变。
UPD#2
我为这个问题编写了一个强力解决方案。但似乎应该有一个简单的出路。无论如何,这是解决方案(也可以添加'alpha'
频道)。
def generateColor(color):
nstep = 300
minW = 400
maxW = 700
bandW = maxW - minW
colorTuple = ()
for i in range(nstep + 1):
wlength = minW + i * bandW / nstep
colorTuple += ((1.0 * i / nstep, wav2RGB(wlength)[color] / 255.0, wav2RGB(wlength)[color] / 255.0),)
return colorTuple
def generateGradient():
return {'red': generateColor(0),
'green': generateColor(1),
'blue': generateColor(2)}
visibleSpec = LinearSegmentedColormap('visible', generateGradient())
这会生成colormap
个实例,您只需将其用作cmap=visibleSpec
即可。函数wav2RGB
只需要以nm为单位的波长,并返回整数数组[R,G,B]
。这是它(source)。
def wav2RGB(Wavelength):
Gamma = 0.80
IntensityMax = 255.0
def Adjust(Color, Factor):
if Color == 0.0:
return 0.0
else:
return round(IntensityMax * (Color * Factor)**Gamma)
if 380 <= trunc(Wavelength) and trunc(Wavelength) <= 439:
Red = -(Wavelength - 440.0) / (440.0 - 380.0)
Green = 0.0
Blue = 1.0
elif 440 <= trunc(Wavelength) and trunc(Wavelength) <= 489:
Red = 0.0
Green = (Wavelength - 440.0) / (490.0 - 440.0)
Blue = 1.0
elif 490 <= trunc(Wavelength) and trunc(Wavelength) <= 509:
Red = 0.0
Green = 1.0
Blue = -(Wavelength - 510.0) / (510.0 - 490.0)
elif 510 <= trunc(Wavelength) and trunc(Wavelength) <= 579:
Red = (Wavelength - 510.0) / (580.0 - 510.0)
Green = 1.0
Blue = 0.0
elif 580 <= trunc(Wavelength) and trunc(Wavelength) <= 644:
Red = 1.0
Green = -(Wavelength - 645.0) / (645.0 - 580.0)
Blue = 0.0
elif 645 <= trunc(Wavelength) and trunc(Wavelength) <= 780:
Red = 1.0
Green = 0.0
Blue = 0.0
else:
Red = 0.0
Green = 0.0
Blue = 0.0
# Let the intensity fall off near the vision limits
if 380 <= trunc(Wavelength) and trunc(Wavelength) <= 419:
factor = 0.3 + 0.7*(Wavelength - 380.0) / (420.0 - 380.0)
elif 420 <= trunc(Wavelength) and trunc(Wavelength) <= 700:
factor = 1.0
elif 701 <= trunc(Wavelength) and trunc(Wavelength) <= 780:
factor = 0.3 + 0.7*(780.0 - Wavelength) / (780.0 - 700.0)
else:
factor = 0.0
R = Adjust(Red, factor)
G = Adjust(Green, factor)
B = Adjust(Blue, factor)
return [int(R), int(G), int(B)]
答案 0 :(得分:1)
您是否考虑过制作色谱图中的色彩图? 它并不完美,但它可能对你有所帮助:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
def make_colormap(seq, add_first_last=True, alpha=False,
name='CustomMap', register=True, N=256):
"""Return a LinearSegmentedColormap
seq: a sequence of floats and RGB-tuples. The floats should be increasing
and in the interval (0,1).
FROM: http://stackoverflow.com/questions/16834861/
Adapted with help from:
http://matplotlib.org/examples/pylab_examples/custom_cmap.html
"""
if add_first_last:
seq = [(None,) * 3, 0.0] + list(seq) + [1.0, (None,) * 3]
cdict = {'red': [], 'green': [], 'blue': []}
if alpha:
cdict['alpha']=[]
for i, item in enumerate(seq):
if isinstance(item, float):
if alpha:
r1, g1, b1, a1 = seq[i - 1]
r2, g2, b2, a2 = seq[i + 1]
cdict['alpha'].append([item, a1, a2])
else:
r1, g1, b1 = seq[i - 1]
r2, g2, b2 = seq[i + 1]
cdict['red'].append([item, r1, r2])
cdict['green'].append([item, g1, g2])
cdict['blue'].append([item, b1, b2])
colormap = mcolors.LinearSegmentedColormap(name, cdict, N=N)
if register:
plt.register_cmap(cmap=colormap)
return colormap
# Convert wavelength to color
def lin_inter(value, left=0., right=1., increase=True):
"""
Returns the fractional position of ``value`` between ``left`` and
``right``, increasing from 0 if ``value==left`` to 1 if ``value==right``,
or decreasing from 1 to zero if not ``increase``.
"""
if increase:
return (value - left) / (right - left)
else:
return (right - value) / (right - left)
def wav2RGB(Wavelength, upto255=False, Gamma=1.0):
"""
Converts an wavelength to an RGB list, with fractional values between
0 and 1 if not ``upto255``, or int values between 0 and 255 if ``upto255``
"""
# Check the interval the color is in
if 380 <= Wavelength < 440:
# red goes from 1 to 0:
Red = lin_inter(Wavelength, 380., 440., False)
Green = 0.0
Blue = 1.0
elif 440 <= Wavelength < 490:
Red = 0.0
# green goes from 0 to 1:
Green = lin_inter(Wavelength, 440., 490., True)
Blue = 1.0
elif 490 <= Wavelength < 510:
Red = 0.0
Green = 1.0
Blue = lin_inter(Wavelength, 490., 510., False)
elif 510 <= Wavelength < 580:
Red = lin_inter(Wavelength, 510., 580., True)
Green = 1.0
Blue = 0.0
elif 580 <= Wavelength < 645:
Red = 1.0
Green = lin_inter(Wavelength, 580., 645., False)
Blue = 0.0
elif 645 <= Wavelength <= 780:
Red = 1.0
Green = 0.0
Blue = 0.0
else: # Wavelength < 380 or Wavelength > 780
Red = 0.0
Green = 0.0
Blue = 0.0
# Let the intensity fall off near the vision limits
if 380 <= Wavelength < 420:
factor = 0.3 + 0.7*lin_inter(Wavelength, 380., 420., True)
elif 420 <= Wavelength < 700:
factor = 1.0
elif 700 <= Wavelength <= 780:
factor = 0.3 + 0.7*lin_inter(Wavelength, 700., 780., False)
else:
factor = 0.0
# Adjust color intensity
if upto255:
def Adjust(Color, Factor):
return int(round(255. * (Color * Factor)**Gamma))
else:
def Adjust(Color, Factor):
return (Color * Factor)**Gamma
R = Adjust(Red, factor)
G = Adjust(Green, factor)
B = Adjust(Blue, factor)
#return color
return [R, G, B]
# Create colormap based on function above:
wl_breakpoints = [380, 420, 440, 490, 510, 580, 645,700,780]
breakpoints = [lin_inter(point, 380., 780.) for point in wl_breakpoints]
colors = [wav2RGB(point) for point in wl_breakpoints]
N = len(breakpoints)
seq = []
for i in xrange(N):
seq.extend((colors[i], breakpoints[i], colors[i]))
make_colormap(seq=seq, add_first_last=False, alpha=False,
name='CustomSpectrum', register=True)
# Draw colormaps
def compare_spectrums(cmaps=[], n_points=256):
"""
adapted from:
http://matplotlib.org/1.3.0/examples/color/colormaps_reference.html
"""
nrows = max(len(cmap_list) for cmap_category, cmap_list in cmaps)
gradient = np.linspace(0, 1, n_points)
gradient = np.vstack((gradient, gradient))
def plot_color_gradients(cmap_category, cmap_list):
fig, axes = plt.subplots(nrows=nrows)
fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99)
axes[0].set_title(cmap_category + ' colormaps', fontsize=14)
for ax, name in zip(axes, cmap_list):
ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(name))
pos = list(ax.get_position().bounds)
x_text = pos[0] - 0.01
y_text = pos[1] + pos[3]/2.
fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10)
#Turn off *all* ticks & spines, not just the ones with colormaps.
for ax in axes:
ax.set_axis_off()
for cmap_category, cmap_list in cmaps:
plot_color_gradients(cmap_category, cmap_list)
plt.show()
if __name__ == '__main__':
# Create random spectrum
n_points = 1000
wavelengths = np.linspace(380, 780, n_points)
spectrum = np.random.random(n_points)
def color_and_intensity(wavelength):
return [c*np.interp(x=wavelength, xp=wavelengths, fp=spectrum)\
for c in wav2RGB(wavelength)]
colors = [color_and_intensity(point) for point in wavelengths]
breakpoints = [lin_inter(point, 380., 780.) for point in wavelengths]
N = len(breakpoints)
seq = []
for i in xrange(N):
seq.extend((colors[i], breakpoints[i], colors[i]))
make_colormap(seq=seq, add_first_last=False, alpha=False,
name='RandomSpectrum', register=True, N=n_points)
cmaps = [('Spectrum', ['spectral','gist_rainbow',
'CustomSpectrum','RandomSpectrum'])]
compare_spectrums(cmaps=cmaps, n_points=n_points)