Самая быстрая формула для получения Hue из RGB

Если вам даны красные, зеленые и синие значения, которые колеблются от 0 до 255, что было бы самым быстрым вычислением, чтобы получить только значение оттенка? Эта формула будет использоваться на каждом пикселе изображения 640x480 со скоростью 30 кадров в секунду (9,2 млн. Раз в секунду), поэтому каждый бит оптимизации скорости помогает.

Я видел другие формулы, но я не доволен тем, сколько шагов они задействуют. Я ищу фактическую формулу, а не встроенную библиотечную функцию.

Ответы

Ответ 1

  • Преобразуйте значения RGB в диапазон 0-1, это можно сделать, разделив значение на 255 для 8-битовой глубины цвета (r, g, b - заданные значения):

    R = r / 255 = 0.09
    G = g / 255 = 0.38
    B = b / 255 = 0.46
  • Найдите минимальное и максимальное значения R, G и B.

  • В зависимости от того, какой цветной канал RGB является максимальным. Три различные формулы: If Red is max, then Hue = (G-B)/(max-min) If Green is max, then Hue = 2.0 + (B-R)/(max-min) If Blue is max, then Hue = 4.0 + (R-G)/(max-min)

Значение оттенка, которое вы получаете, должно быть умножено на 60, чтобы преобразовать его в градусы по цветному кругу. Если оттенок становится отрицательным, вам нужно добавить 360, потому что круг имеет 360 градусов.

Вот полная статья.

Ответ 2

В дополнение к ответу Умряева:

Если требуется только оттенок, не требуется разделять цвета 0-255 с диапазоном 255.

Результат e.x. (green - blue) / (max - min) будет одинаковым для любого диапазона (если цвета находятся в одном и том же диапазоне).

Вот пример java для получения оттенка:

public int getHue(int red, int green, int blue) {

    float min = Math.min(Math.min(red, green), blue);
    float max = Math.max(Math.max(red, green), blue);

    if (min == max) {
        return = 0;
    }

    float hue = 0f;
    if (max == red) {
        hue = (green - blue) / (max - min);

    } else if (max == green) {
        hue = 2f + (blue - red) / (max - min);

    } else {
        hue = 4f + (red - green) / (max - min);
    }

    hue = hue * 60;
    if (hue < 0) hue = hue + 360;

    return Math.round(hue);
}

Изменить: добавлена ​​проверка, являются ли min и max одинаковыми, так как в этом случае остальная часть расчета не нужна, и чтобы избежать деления на 0 (см. комментарии)

Ответ 3

// convert rgb values to the range of 0-1
var h;
r /= 255, g /= 255, b /= 255;

// find min and max values out of r,g,b components
var max = Math.max(r, g, b), min = Math.min(r, g, b);

if(max == r){
    // if red is the predominent color
    h = (g-b)/(max-min);
}
else if(max == g){
    // if green is the predominent color
    h = 2 +(b-r)/(max-min);
}
else if(max == b){
    // if blue is the predominent color
    h = 4 + (r-g)/(max-min);
}

h = h*60; // find the sector of 60 degrees to which the color belongs
// https://www.pathofexile.com/forum/view-thread/1246208/page/45 - hsl color wheel

if(h > 0){
    // h is a positive angle in the color wheel
    return Math.floor(h);
}
else{
    // h is a negative angle.
    return Math.floor(360 -h);
}

Ответ 4

Вы должны указать, какой язык вам нужен. С#, Java и C - очень разные языки и могут работать на разных платформах.

640x480 не очень большой по сравнению с текущими общими разрешениями. Вы должны попробовать все возможные решения и бенчмарк. Алгоритм, который имеет много шагов, не обязательно медленнее, чем более короткий, поскольку цикл команд не является фиксированным, и есть много других факторов, которые влияют на производительность, такие как когерентность кэша.

Для упомянутого выше алгоритма Умряева вы можете заменить деление на 255 умножением на 1.0/255, что улучшит производительность с допустимой ошибкой.

В C вы можете сделать векторизацию, чтобы улучшить ее еще больше. Вы также можете использовать аппаратное ускорение.

В С# и Java у вас не так много вариантов. Вы можете запустить небезопасный код на С#, или если вы используете Mono, вы можете использовать векторный тип, который уже поддерживает SSE. В Java вы можете запускать собственный код через JNI