我获得了一个基本上是图像的数据集,但是图像中的每个像素都表示为从-1到1的值。我正在编写一个应用程序,需要采用这些-1到1灰度值,并将它们映射到MATLAB“Jet”色标(红 - 绿 - 蓝色渐变)的相关RGB值。
我很好奇是否有人知道如何获取线性值(如-1到1)并将其映射到此比例。请注意,我实际上并没有使用MATLAB(我也不能),我只需要获取灰度值并将其放在Jet渐变上。
谢谢, 亚当
答案 0 :(得分:69)
考虑以下功能(由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);
}
在您的情况下,您可以使用它将[-1,1]
范围内的值映射为颜色(将C代码转换为MATLAB函数很简单):
c = GetColour(v,-1.0,1.0);
这会产生以下“从热到冷”的颜色渐变:
它基本上表示RGB颜色立方体的边缘从蓝色到红色(通过青色,绿色,黄色),并沿此路径插值。
请注意,这与MATLAB中使用的“Jet”色彩图略有不同,据我所知,它通过以下路径:
#00007F: dark blue
#0000FF: blue
#007FFF: azure
#00FFFF: cyan
#7FFF7F: light green
#FFFF00: yellow
#FF7F00: orange
#FF0000: red
#7F0000: dark red
以下是我在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
然后我们使用:
绘制两者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)
现在你可以修改上面的C代码,并使用建议的停止点来实现类似于jet colormap的东西(它们都在R,G,B通道上使用线性插值,如上图所示)。
答案 1 :(得分:21)
我希望这就是你要找的东西:
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;
}
我不确定这个比例是否与您链接的图像完全相同,但看起来应该非常相似。
<强>更新强> 我根据MatLab的Jet调色板here
的描述重写了代码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 );
}
答案 2 :(得分:7)
其他答案将插值视为分段线性函数。通过使用夹紧三角形基函数进行插值可以简化这一过程。我们需要一个钳制功能,将其输入映射到闭合单位间隔:
插值的基函数:
然后颜色变为:
将其从-1绘制为1给出:
与this answer中提供的内容相同。使用an efficient clamp implementation:
double clamp(double v)
{
const double t = v < 0 ? 0 : v;
return t > 1.0 ? 1.0 : t;
}
并确保您的值 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));
如上面关于实现clamp
的链接所示,编译器可以优化分支。编译器还可以使用内在函数来设置std::abs
的符号位,从而消除另一个分支。
类似的治疗方法可以用于“热到冷”的治疗。颜色映射。在这种情况下,基础和颜色功能是:
[-1,1]的热到冷的情节:
消除显式分支使得这种方法可以有效地实现为OpenGL着色器程序。 GLSL为在{3}向量上运行的abs
和clamp
提供内置函数。矢量化颜色计算并优先选择内置函数而不是分支可以显着提高性能。下面是GLSL中的一种实现,它将RGB喷射颜色作为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));
}
答案 3 :(得分:2)
我不确定为什么这个简单的等式有这么多复杂的答案。基于Amro评论(谢谢)上面发布的MatLab JET热 - 冷色彩图和图表,使用高速/基本数学计算RGB值非常简单。
我使用以下函数进行实时渲染标准化数据以显示频谱图,并且它具有令人难以置信的快速和高效,在双精度乘法和除法之外没有复杂的数学运算,通过三元逻辑链简化。这段代码是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;
}
该函数采用每个JET颜色规范的-1.0到1.0的序数范围,但如果你超出该范围(我在此处调用之前这样做),此函数不会进行健全性检查。
因此,请确保在调用此函数之前进行完整性/边界检查,或者只是添加自己的限制以在自己实现时限制值。
这种实现并没有考虑到光度,因此可能不会被认为是纯粹的实现,但会让你在相当好的情况下更快地进入球场。
答案 4 :(得分:1)
好像你有HSL系统的色调值,而且饱和度和亮度是隐含的。在互联网上搜索HSL到RGB转换,你会发现很多解释,代码等(这里是one link)
在您的特定情况下,我们假设您将所有颜色饱和度默认为1,亮度为0.5。以下是可用于获取RGB值的公式:
想象一下,对于每个像素,您都有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;
}
并且RGB
中的RGB值全部在[0,1]范围内。请注意,原始转换更复杂,我根据饱和度= 1 和亮度= 0.5
为什么这个公式?见wikipedia entry
答案 5 :(得分:0)
这可能不完全相同,但可能足够接近您的需求:
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;
}
答案 6 :(得分:0)
将生成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)