Самая быстрая формула для получения 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