Оттенки серого до красно-зелено-синего (MATLAB Jet)

28

Мне был предоставлен набор данных, который по существу является изображением, однако каждый пиксель в изображении представлен как значение от -1 до 1 включительно. Я пишу приложение, которое должно принимать эти значения от -1 до 1 в оттенках серого и сопоставлять их со связанным значением RGB для цветовой шкалы MATLAB "Jet" (красно-зелено-синий цветовой градиент).

Мне любопытно, знает ли кто, как взять линейное значение (например, от -1 до 1) и сопоставить его с этим масштабом. Обратите внимание, что я на самом деле не использую MATLAB для этого (и не могу), мне просто нужно взять значение оттенков серого и поместить его в градиент Jet.

Спасибо, Адам

  • 0
    Итак, вы хотите получить черно-белое изображение в цветном формате?
  • 1
    Что означают значения? Например, -1 чисто черный?
Показать ещё 4 комментария
Теги:
colors

6 ответов

17
Лучший ответ

Надеюсь, это то, что вы ищете:

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 Я переписал код в соответствии с описанием палитры 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 );
}
  • 0
    Это довольно близко, но не 100%. Это будет работать в настоящее время. Возможно, мне просто нужно попробовать текстуру, чтобы получить точные значения ... Спасибо!
  • 2
    Я нашел некоторую информацию о палитре Jet MatLab: bugs.launchpad.net/inkscape/+bug/236508
63

Рассмотрим следующую функцию (написанную 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);

Это приводит к следующей цветовой рампе "горячая-холодная":

Изображение 6399

В основном это представляет собой прогулку по краям цветового куба RGB от синего до красного (проходящего по голубым, зеленым, желтым) и интерполирования значений по этому пути.

Изображение 6400


Обратите внимание, что это немного отличается от цветовой карты "Jet", используемой в MATLAB, которая, насколько я могу судить, проходит по следующему пути:

#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)

Изображение 6401Изображение 6402

Теперь вы можете изменить приведенный выше код C и использовать предложенные точки остановки для достижения чего-то похожего на струйную карту (все они используют линейную интерполяцию по каналам R, G, B, как вы можете видеть из приведенных выше графиков)..

  • 0
    MATLAB Jet следует по этому пути согласно [ bugs.launchpad.net/inkscape/+bug/236508](this) .11 шагам в СИНЕЙ линии от (0, 0, 127) до (0, 0, 255), СИНИЙ - ЗЕЛЕНЫЙ линия от (0, 14, 255) до (0, 255, 255) за 18 шагов, зеленая - желтая линия от (14, 255, 240) до (255, 255, 0) за 18 шагов, желтая - красная линия от (255, 240, 0) - (255, 0, 0) за 18 шагов и, наконец, по красной линии (240, 0, 0) - (127, 0, 0) за 11 шагов. Может быть небольшая ошибка в количестве шагов.
  • 1
    имейте в виду, что MATLAB интерполирует значения по пути, указанному в ответе выше (см. график линейного изменения), и возвращает цветовую карту любой длины, значения не жестко запрограммированы на фиксированной длине.
4

Другие ответы рассматривают интерполяцию как кусочно-линейную функцию. Это можно упростить, используя зажатую треугольную базовую функцию для интерполяции. Нам нужна функция зажима, которая отображает его вход в замкнутый единичный интервал:

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

И базовая функция для интерполяции:

[IMG_OUR_ID=6403.5%20-%20%7C2t%7C%29]

Затем цвет становится:

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

Задание этого значения от -1 до 1 дает:

Изображение 6404

То, что указано в этом ответе. Используя эффективную реализацию зажима:

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, компилятор может оптимизировать ветки. Компилятор также может использовать intrinsics для установки знакового бита для std::abs устранения другой ветки.

"Горячие к холодной"

Аналогичное лечение можно использовать для "горячего-холодного" цветного отображения. В этом случае базовые и цветовые функции:

[IMG_OUR_ID=6405.latex?%5Clarge%20N%28t%29%20%26%3D%20%5Coperatorname%7Bclamp%7D_%7B%5B0%2C1%5D%7D%282%20-%20%7C2t%7C%29%5C%5C]

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

И горячий-холодный график для [-1, 1]:

Изображение 6406

Программа шейдера OpenGL

Устранение явных ветвей делает этот подход эффективным для реализации в качестве программы шейдеров OpenGL. GLSL предоставляет встроенные функции для abs и clamp, которые работают с 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));
}
1

Я не совсем уверен, почему так много сложных ответов на это простое уравнение. Основываясь на диаграмме цветной карты MatLab JET "Горячая-Холодная" и графике, вышедшем выше в комментарии Amro (спасибо), логика очень проста для вычисления значений RGB с использованием высокоскоростной/базовой математики.

Я использую следующую функцию для нормальных данных рендеринга в реальном времени для отображения спектрограмм, и это невероятно быстро и эффективно, без сложной математики за пределами двойной точности умножения и деления, упрощенной тройной логической цепью. Этот код является С#, но очень легко переносится практически на любой другой язык (извините 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;
}

Функция принимает порядковый диапазон от -1,0 до 1,0 для спецификации цвета JET, хотя эта функция не проверяет работоспособность, если вы находитесь за пределами этого диапазона (я делаю это до моего вызова здесь).

Поэтому убедитесь, что вы проверили проверку работоспособности/границы до вызова этой функции или просто добавили свой собственный лимит, чтобы ограничить значение при его реализации.

Эта реализация не учитывает светимость, поэтому не может считаться реализацией пуриста, но довольно хорошо вы получаете в шале и намного быстрее.

1

Похоже, что у вас есть значения оттенка системы HSL, а насыщенность и легкость неявные. Найдите преобразование HSL в RGB в Интернете, и вы найдете много объяснений, кода и т.д. (Здесь одна ссылка)

В вашем конкретном случае, однако, допустим, что вы по умолчанию выполняете все насыщения цвета до 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 и lightness = 0.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;
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню