C++ 灰度到红绿蓝 (MATLAB Jet) 色标
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7706339/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Grayscale to Red-Green-Blue (MATLAB Jet) color scale
提问by Adam Shook
I was given a data set that is essentially an image, however each pixel in the image is represented as a value from -1 to 1 inclusive. I am writing an application that needs to take these -1 to 1 grayscale values and map them to the associated RGB value for the MATLAB "Jet" color scale (red-green-blue color gradient).
我得到了一个数据集,它本质上是一个图像,但是图像中的每个像素都表示为从 -1 到 1 的值。我正在编写一个应用程序,它需要采用这些 -1 到 1 的灰度值并将它们映射到 MATLAB“Jet”色标(红-绿-蓝颜色渐变)的关联 RGB 值。
I am curious if anyone knows how to take a linear value (like -1 to 1) and map it to this scale. Note that I am not actually using MATLAB for this (nor can I), I just need to take the grayscale value and put it on the Jet gradient.
我很好奇是否有人知道如何取一个线性值(如 -1 到 1)并将其映射到这个比例。请注意,我实际上并没有为此使用 MATLAB(我也不能),我只需要获取灰度值并将其放在 Jet 渐变上。
Thanks, Adam
谢谢,亚当
采纳答案by Ilya Denisov
I hope this is what you're looking for:
我希望这是你正在寻找的:
double interpolate( double val, double y0, double x0, double y1, double x1 ) {
return (val-x0)*(y1-y0)/(x1-x0) + y0;
}
double blue( double grayscale ) {
if ( grayscale < -0.33 ) return 1.0;
else if ( grayscale < 0.33 ) return interpolate( grayscale, 1.0, -0.33, 0.0, 0.33 );
else return 0.0;
}
double green( double grayscale ) {
if ( grayscale < -1.0 ) return 0.0; // unexpected grayscale value
if ( grayscale < -0.33 ) return interpolate( grayscale, 0.0, -1.0, 1.0, -0.33 );
else if ( grayscale < 0.33 ) return 1.0;
else if ( grayscale <= 1.0 ) return interpolate( grayscale, 1.0, 0.33, 0.0, 1.0 );
else return 1.0; // unexpected grayscale value
}
double red( double grayscale ) {
if ( grayscale < -0.33 ) return 0.0;
else if ( grayscale < 0.33 ) return interpolate( grayscale, 0.0, -0.33, 1.0, 0.33 );
else return 1.0;
}
I'm not sure if this scale is 100% identical to the image you linked but it should look very similar.
我不确定这个比例是否与您链接的图像 100% 相同,但它看起来应该非常相似。
UPDATEI've rewritten the code according to the description of MatLab's Jet palette found here
更新我根据此处找到的 MatLab Jet 调色板的描述重写了代码
double interpolate( double val, double y0, double x0, double y1, double x1 ) {
return (val-x0)*(y1-y0)/(x1-x0) + y0;
}
double base( double val ) {
if ( val <= -0.75 ) return 0;
else if ( val <= -0.25 ) return interpolate( val, 0.0, -0.75, 1.0, -0.25 );
else if ( val <= 0.25 ) return 1.0;
else if ( val <= 0.75 ) return interpolate( val, 1.0, 0.25, 0.0, 0.75 );
else return 0.0;
}
double red( double gray ) {
return base( gray - 0.5 );
}
double green( double gray ) {
return base( gray );
}
double blue( double gray ) {
return base( gray + 0.5 );
}
回答by Amro
Consider the following function (written by Paul Bourke-- search for Colour Ramping for Data Visualisation
):
考虑以下函数(由Paul Bourke编写——搜索Colour Ramping for Data Visualisation
):
/*
Return a RGB colour value given a scalar v in the range [vmin,vmax]
In this case each colour component ranges from 0 (no contribution) to
1 (fully saturated), modifications for other ranges is trivial.
The colour is clipped at the end of the scales if v is outside
the range [vmin,vmax]
*/
typedef struct {
double r,g,b;
} COLOUR;
COLOUR GetColour(double v,double vmin,double vmax)
{
COLOUR c = {1.0,1.0,1.0}; // white
double dv;
if (v < vmin)
v = vmin;
if (v > vmax)
v = vmax;
dv = vmax - vmin;
if (v < (vmin + 0.25 * dv)) {
c.r = 0;
c.g = 4 * (v - vmin) / dv;
} else if (v < (vmin + 0.5 * dv)) {
c.r = 0;
c.b = 1 + 4 * (vmin + 0.25 * dv - v) / dv;
} else if (v < (vmin + 0.75 * dv)) {
c.r = 4 * (v - vmin - 0.5 * dv) / dv;
c.b = 0;
} else {
c.g = 1 + 4 * (vmin + 0.75 * dv - v) / dv;
c.b = 0;
}
return(c);
}
Which, in your case, you would use it to map values in the range [-1,1]
to colors as (it is straightforward to translate it from C code to a MATLAB function):
在您的情况下,您将使用它来将范围内的值映射[-1,1]
到颜色(将其从 C 代码转换为 MATLAB 函数很简单):
c = GetColour(v,-1.0,1.0);
This produces to the following "hot-to-cold" color ramp:
这会产生以下“热到冷”色带:
It basically represents a walk on the edges of the RGB color cube from blue to red (passing by cyan, green, yellow), and interpolating the values along this path.
它基本上代表了在 RGB 颜色立方体的边缘上从蓝色到红色(经过青色、绿色、黄色)的行走,并沿着这条路径对值进行插值。
Note this is slightly different from the "Jet" colormap used in MATLAB, which as far as I can tell, goes through the following path:
请注意,这与 MATLAB 中使用的“Jet”颜色图略有不同,据我所知,它通过以下路径:
#00007F: dark blue
#0000FF: blue
#007FFF: azure
#00FFFF: cyan
#7FFF7F: light green
#FFFF00: yellow
#FF7F00: orange
#FF0000: red
#7F0000: dark red
Here is a comparison I did in MATLAB:
这是我在 MATLAB 中做的一个比较:
%# values
num = 64;
v = linspace(-1,1,num);
%# colormaps
clr1 = jet(num);
clr2 = zeros(num,3);
for i=1:num
clr2(i,:) = GetColour(v(i), v(1), v(end));
end
Then we plot both using:
然后我们使用以下方法绘制两者:
figure
subplot(4,1,1), imagesc(v), colormap(clr), axis off
subplot(4,1,2:4), h = plot(v,clr); axis tight
set(h, {'Color'},{'r';'g';'b'}, 'LineWidth',3)
Now you can modify the C code above, and use the suggested stop points to achieve something similar to jet colormap (they all use linear interpolation over the R,G,B channels as you can see from the above plots)...
现在您可以修改上面的 C 代码,并使用建议的停止点来实现类似于 jet 颜色图的效果(它们都使用 R、G、B 通道上的线性插值,如您从上面的图中所见)...
回答by Joshua Fraser
The other answers treat the interpolation as a piecewise linear function. This can be simplified by using a clamped triangular basis function for interpolation. We need a clamp function that maps its input to the closed unit interval:
其他答案将插值视为分段线性函数。这可以通过使用钳位三角基函数进行插值来简化。我们需要一个将其输入映射到闭单位区间的钳位函数:
And a basis function for interpolation:
以及用于插值的基函数:
Then the color becomes:
然后颜色变成:
Plotting this from -1 to 1 gives:
将其从 -1 绘制到 1 给出:
Which is the same as provided in this answer. Using an efficient clamp implementation:
double clamp(double v)
{
const double t = v < 0 ? 0 : v;
return t > 1.0 ? 1.0 : t;
}
and ensuring your value tis in [-1, 1], then jet color is simply:
并确保您的值t在 [-1, 1] 中,那么喷气颜色很简单:
double red = clamp(1.5 - std::abs(2.0 * t - 1.0));
double green = clamp(1.5 - std::abs(2.0 * t));
double blue = clamp(1.5 - std::abs(2.0 * t + 1.0));
As shown in the above link on implementing clamp
, the compiler may optimize out branches. The compiler may also use intrinsics to set the sign bit for std::abs
eliminating another branch.
如上面关于实现的链接所示clamp
,编译器可能会优化分支。编译器还可以使用内部函数来设置符号位以std::abs
消除另一个分支。
"Hot-to-Cold"
“热到冷”
A similar treatment can be used for the "hot-to-cold" color mapping. In this case the basis and color functions are:
类似的处理可用于“热到冷”颜色映射。在这种情况下,基函数和颜色函数是:
And the hot-to-cold plot for [-1, 1]:
以及 [-1, 1] 的热到冷图:
OpenGL Shader Program
OpenGL 着色器程序
Eliminating explicit branches makes this approach efficient for implementing as an OpenGL shader program. GLSL provides built-in functions for both abs
and clamp
that operate on 3D vectors. Vectorizing the color calculation and preferring built-in functions over branching can provide significant performance gains. Below is an implementation in GLSL that returns the RGB jet color as a vec3
. Note that the basis function was modified such that tmust lie in [0,1] rather than the range used in the other examples.
消除显式分支使这种方法可以有效地作为 OpenGL 着色器程序实现。GLSL 为3D 向量abs
和clamp
3D 向量提供了内置函数。对颜色计算进行矢量化并优先使用内置函数而不是分支可以显着提高性能。下面是 GLSL 中的一个实现,它将 RGB jet 颜色作为vec3
. 请注意,修改了基函数,使得t必须位于 [0,1] 内,而不是在其他示例中使用的范围内。
vec3 jet(float t)
{
return clamp(vec3(1.5) - abs(4.0 * vec3(t) + vec3(-3, -2, -1)), vec3(0), vec3(1));
}
回答by tpartee
I'm not really sure why there are so many complex answers to this simple equation. Based on the MatLab JET Hot-to-Cold color map chart and graph plot posted above in Amro's comment (thank you), the logic is very simple to calculate the RGB values using high-speed/basic math.
我不太确定为什么这个简单的等式有这么多复杂的答案。基于上面 Amro 评论中发布的 MatLab JET 热到冷颜色图和图表(谢谢),使用高速/基本数学计算 RGB 值的逻辑非常简单。
I use the following function for live-rendering normalized data to display spectrograms and it's incredibly fast and efficient with no complex math outside double precision multiplication and division, simplified by ternary logic chaining. This code is C# but very easily ported to almost any other language (sorry PHP programmers, you're out of luck thanks to abnormal ternary chain order).
我使用以下函数实时渲染归一化数据以显示频谱图,它非常快速和高效,除了双精度乘法和除法之外没有复杂的数学,通过三元逻辑链简化。这段代码是 C# 但很容易移植到几乎任何其他语言(对不起 PHP 程序员,由于异常的三元链顺序,你运气不好)。
public byte[] GetMatlabRgb(double ordinal)
{
byte[] triplet = new byte[3];
triplet[0] = (ordinal < 0.0) ? (byte)0 : (ordinal >= 0.5) ? (byte)255 : (byte)(ordinal / 0.5 * 255);
triplet[1] = (ordinal < -0.5) ? (byte)((ordinal + 1) / 0.5 * 255) : (ordinal > 0.5) ? (byte)(255 - ((ordinal - 0.5) / 0.5 * 255)) : (byte)255;
triplet[2] = (ordinal > 0.0) ? (byte)0 : (ordinal <= -0.5) ? (byte)255 : (byte)(ordinal * -1.0 / 0.5 * 255);
return triplet;
}
The function takes an ordinal range from -1.0 to 1.0 per the JET color specification, though this function does no sanity checking if you're outside that range (I do that before my call here).
根据 JET 颜色规范,该函数采用从 -1.0 到 1.0 的序数范围,尽管此函数不会检查您是否超出该范围(我在此处调用之前已这样做)。
So make sure you do sanity/bounds checking prior to calling this function or simply add your own limiting to cap the value when you implement it yourself.
因此,请确保在调用此函数之前进行完整性/边界检查,或者在您自己实现时简单地添加您自己的限制来限制该值。
This implementation does not take luminosity into consideration so may not be considered a purist implementation but gets you in the ballpark fairly well and is much faster.
这个实现没有考虑亮度,所以可能不被认为是一个纯粹的实现,但让你在球场上相当好,而且速度要快得多。
回答by Shahbaz
Seems like you have hue values of an HSL system and the saturation and lightness are implicit. Search for HSL to RGB conversion on the internet and you will find a lot of explanations, code etc. (Here is one link)
好像你有一个 HSL 系统的色调值,饱和度和亮度是隐含的。在网上搜索HSL to RGB转换,会发现很多解释,代码等(这里有一个链接)
In your particular case, though, let's assume you are defaulting all color saturations to 1 and lightness to 0.5. Here is the formula you can use to get the RGB values:
但是,在您的特定情况下,让我们假设您将所有颜色饱和度默认为 1,亮度为 0.5。以下是可用于获取 RGB 值的公式:
Imagine for every pixel, you have h
the value you read from your data.
想象一下,对于每个像素,您都有h
从数据中读取的值。
hue = (h+1.0)/2; // This is to make it in range [0, 1]
temp[3] = {hue+1.0/3, hue, hue-1.0/3};
if (temp[0] > 1.0)
temp[0] -= 1.0;
if (temp[2] < 0.0)
temp[2] += 1.0;
float RGB[3];
for (int i = 0; i < 3; ++i)
{
if (temp[i]*6.0 < 1.0)
RGB[i] = 6.0f*temp[i];
else if (temp[i]*2.0 < 1.0)
RGB[i] = 1;
else if (temp[i]*3.0 < 2.0)
RGB[i] = ((2.0/3.0)-temp[i])*6.0f;
else
RGB[i] = 0;
}
And there you have the RGB values in RGB
all in the range [0, 1]. Note that the original conversion is more complex, I simplified it based on values of saturation=1and lightness=0.5
在那里你有RGB
[0, 1] 范围内的所有RGB 值。请注意,原始转换更复杂,我根据饱和度 = 1和亮度 = 0.5 的值对其进行了简化
Why this formula? See this wikipedia entry
为什么是这个公式?请参阅此维基百科条目
回答by IronMensan
This probably isn't exactly the same, but it may be close enough for your needs:
这可能不完全相同,但它可能足以满足您的需求:
if (-0.75 > value) {
blue = 1.75 + value;
} else if (0.25 > value) {
blue = 0.25 - value;
} else {
blue = 0;
}
if ( -0.5 > value) {
green = 0;
} else if (0.5 > value) {
green = 1 - 2*abs(value);
} else {
green = 0;
}
if ( -0.25 > value) {
red = 0;
} else if (0.75 > value) {
red = 0.25 + value;
} else {
red = 1.75 - value;
}
回答by schornagel86
Java(Processing) code that will generate Jet and HotAndCold RGB. I created this code following the RGB distribution scheme in the post of Amro above.
将生成 Jet 和 HotAndCold RGB 的 Java(处理)代码。我按照上面 Amro 帖子中的 RGB 分配方案创建了此代码。
color JetColor(float v,float vmin,float vmax){
float r=0, g=0, b=0;
float x = (v-vmin)/(vmax-vmin);
r = 255*constrain(-4*abs(x-0.75) + 1.5,0,1);
g = 255*constrain(-4*abs(x-0.50) + 1.5,0,1);
b = 255*constrain(-4*abs(x-0.25) + 1.5,0,1);
return color(r,g,b);
}
color HeatColor(float v,float vmin,float vmax){
float r=0, g=0, b=0;
float x = (v-vmin)/(vmax-vmin);
r = 255*constrain(-4*abs(x-0.75) + 2,0,1);
g = 255*constrain(-4*abs(x-0.50) + 2,0,1);
b = 255*constrain(-4*abs(x) + 2,0,1);
return color(r,g,b);
}
//Values are calculated on trapezoid cutoff points in format y=constrain(a(x-t)+b,0,1)
//Where a=((delta)y/(delta)x), t=x-offset value to symetric middle of trapezoid, and b=y-a(x-t) for the last peak point (x,y)