首页 文章

灰度至红绿蓝(MATLAB Jet)色标

提问于
浏览
32

给我一个基本上是图像的数据集,但是图像中的每个像素都表示为从-1到1的值 . 我正在编写一个需要采用这些-1到1灰度值的应用程序,并将它们映射到MATLAB“Jet”色标(红 - 绿 - 蓝色渐变)的相关RGB值 .

我很好奇,如果有人知道如何取一个线性值(如-1到1)并将其映射到此比例 . 请注意,我实际上并没有使用MATLAB(我也不能),我只需要获取灰度值并将其放在Jet渐变上 .

谢谢,亚当

6 回答

  • 2

    我希望这是你正在寻找的:

    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;
    }
    

    我不确定这个刻度是否与您链接的图像100%相同但它应该看起来非常相似 .

    UPDATE I 've rewritten the code according to the description of MatLab' s找到了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 );
    }
    
  • 66

    考虑以下函数(由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);
    

    这会产生以下“热到冷”颜色斜坡:

    color_ramp

    它基本上表示RGB颜色立方体的边缘从蓝色到红色(通过青色,绿色,黄色),并沿着该路径插值 .

    color_cube


    请注意,这与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)
    

    jet

    hot_to_cold

    现在你可以修改上面的C代码,并使用建议的停止点来实现类似于jet colormap的东西(它们都在R,G,B通道上使用线性插值,如上图所示)...

  • 0

    其他答案将插值视为分段线性函数 . 通过使用夹紧三角形基函数进行插值可以简化这一过程 . 我们需要一个钳制功能,将其输入映射到闭合单位间隔:

    clamp(x)=max(0, min(x, 1))

    插值的基函数:

    N(t) = clamp(1.5 - |2t|)

    然后颜色变成:

    r = N(t - 0.5), g = N(t), b = N(t + 0.5)

    将其从-1绘制为1给出:

    Plot of RGB values from -1 to 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;
    }
    

    确保你的 Value 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 的符号位,从而消除另一个分支 .

    “热到冷”

    类似的处理可用于“热 - 冷”颜色映射 . 在这种情况下,基础和颜色功能是:

    N(t) = clamp(2 - |2t|)

    r(t)=N(t-1), g(t) = N(t), b(t) = N(t+1)

    [-1,1]的热到冷图:

    Hot-to-cold plot

    OpenGL着色器程序

    消除显式分支使得该方法有效地实现为OpenGL着色器程序 . GLSL为 absclamp 提供了对3D矢量进行操作的内置函数 . 矢量化颜色计算并优先选择内置函数而不是分支可以显着提高性能 . 下面是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));
    }
    
  • 6

    我不确定为什么这个简单的等式有这么多复杂的答案 . 基于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的序数范围,但是如果你在该范围之外,那么这个函数不会进行健全性检查(我在这里之前这样做) .

    因此,请确保在调用此函数之前进行完整性/边界检查,或者只是在自己实现时添加自己的限制以限制值 .

    这种实现不需要考虑到光度,所以可能不会被认为是一种纯粹的实施方式,但会让你在球场上相当好并且速度更快 .

  • 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]范围内 . 请注意,原始转换更复杂,我根据 saturation=1lightness=0.5 的值对其进行了简化

    为什么这个公式?见wikipedia entry

  • 20

    这可能不完全相同,但可能足够接近您的需求:

    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;
    }
    

相关问题