Эмулировать алгоритм PhotoShop "Color Range"
Я пытаюсь заменить ручной процесс, выполняемый в PhotoShop, автоматическим процессом, выполняемым на сервере. В настоящее время в PhotoShop инструмент "Цветовой диапазон" используется для выбора диапазона цветов с использованием фактора "Fuzziness" и начиная с черного или белого цвета в зависимости от части процесса.
Мои первоначальные подходы включали как использование пороговых значений для люминесценции в цветовом пространстве L * a * b, так и DE94 между цветом-кандидатом и черным/белым. В обоих случаях я выбрал цвета, которые не следует выбирать и/или не выбирать цвета, которые должны быть.
Моя догадка заключается в том, что я должен использовать конусы вместо сфер для моего выбора.
Может ли кто-нибудь дать представление о том, что делает PhotoShop, и если я направляюсь в правильном направлении? Кроме того, если есть библиотека, чтобы сделать это, это было бы потрясающе, я сейчас пишу это на C.
Ответы
Ответ 1
Из того, что я видел в Photoshop, алгоритм, вероятно, может быть похож на следующий:
- Определите функцию, которая вычисляет близость двух цветов: например, используйте эвклидово расстояние в цветовом пространстве, то есть вычислите расстояние между цветами двух пикселей в пространстве RGB, используя Евклидова формула расстояния.
- Затем отрегулируйте интенсивность каждого пикселя с помощью функции fallof,
например, функция Гаусса. Вам, вероятно, потребуется настроить
некоторые параметры. Чтобы уточнить: вы вычисляете расстояние до двух
пикселей в пространстве RGB (а не на расстоянии в двумерных пиксельных координатах) и
затем подайте это в функцию falloff, которая даст результат
между 0.0 и 1.0. Умножьте все цветовые компоненты текущего
пиксель с результатом функции falloff для него. Сделайте это для
каждый пиксель изображения.
- Если вы хотите добавить параметр диапазона эффекта, просто используйте
эта же функция спада для каждого пикселя снова, но на этот раз подайте это
евклидовое расстояние между выбранным пикселем и текущим
пиксель в 2D-пространстве пикселей (расстояние между пикселями
координаты на изображении).
Если вы хотите выбрать только определенные пиксели, вместо того, чтобы применять эффект непосредственно к пикселям изображения, вы можете сохранить значения спада в матрице double
в диапазоне от 0.0 до 1.0. Затем выберите пороговое значение, выше которого вы выберете данный пиксель.
Например, если шаг 2. для пикселя в координате (x, y) дал 0,8, а шаг 3 дал 0,5, то значение матричного элемента с координатами x и y должно быть 0.8*0.5=0.4
. Если вы выбрали порог выбора ниже 0,4, вы должны выбрать пиксель (x, y), в противном случае вы бы этого не сделали.
Ответ 2
Моя образованная догадка заключается в том, что она использует цветовое пространство HSL, а нечеткость - это параметр, который выбирает все цвета с определенным оттенком и насыщенностью в окне легкости (на основе это).
Теперь выбор может просто делать пороговый расчет, находя все, что находится внутри этого окна (что было бы очень маленькой областью в цветовом пространстве). Однако он также может выполнять статистический расчет расстояния. Если образец цвета на самом деле маленький, образец отображает окно вокруг цвета, вы можете рассчитать дисперсию цвета и использовать что-то вроде Mahalanobis расчет расстояния и пороговое значение от этого.
Опять же, это всего лишь гипотеза, но, возможно, это поможет вашему процессу мышления.
Наконец, в то время как в этой библиотеке не реализовано что-то подобное непосредственно, OpenCV имеет множество инструментов обработки изображений, которые облегчили бы реализацию.
Ответ 3
Я не знаю, как фотошоп делает это под капотом, но это простой RGB, как XYZ 3d-векторный подход:
rDelta = pixel.r - color.r
gDelta = pixel.g - color.g
bDelta = pixel.b - color.b
fuzziness = 0.1 // anything 0 to 1.0
maxDistance = fuzziness * 441 // max distance, black -> white
distance = Math.sqrt(rDelta * rDelta + gDelta * gDelta + bDelta * bDelta)
if (distance < maxDistance) includePixel()
else dontIncludePixel()
Это функция pixel_difference от источника gimp:
https://github.com/GNOME/gimp/blob/125cf2a2a3e1e85172af25871a2cda3638292fdb/app/core/gimpimage-contiguous-region.c#L290
static gfloat
pixel_difference (const gfloat *col1,
const gfloat *col2,
gboolean antialias,
gfloat threshold,
gint n_components,
gboolean has_alpha,
gboolean select_transparent,
GimpSelectCriterion select_criterion)
{
gfloat max = 0.0;
/* if there is an alpha channel, never select transparent regions */
if (! select_transparent && has_alpha && col2[n_components - 1] == 0.0)
return 0.0;
if (select_transparent && has_alpha)
{
max = fabs (col1[n_components - 1] - col2[n_components - 1]);
}
else
{
gfloat diff;
gint b;
if (has_alpha)
n_components--;
switch (select_criterion)
{
case GIMP_SELECT_CRITERION_COMPOSITE:
for (b = 0; b < n_components; b++)
{
diff = fabs (col1[b] - col2[b]);
if (diff > max)
max = diff;
}
break;
case GIMP_SELECT_CRITERION_R:
max = fabs (col1[0] - col2[0]);
break;
case GIMP_SELECT_CRITERION_G:
max = fabs (col1[1] - col2[1]);
break;
case GIMP_SELECT_CRITERION_B:
max = fabs (col1[2] - col2[2]);
break;
case GIMP_SELECT_CRITERION_H:
{
/* wrap around candidates for the actual distance */
gfloat dist1 = fabs (col1[0] - col2[0]);
gfloat dist2 = fabs (col1[0] - 1.0 - col2[0]);
gfloat dist3 = fabs (col1[0] - col2[0] + 1.0);
max = MIN (dist1, dist2);
if (max > dist3)
max = dist3;
}
break;
case GIMP_SELECT_CRITERION_S:
max = fabs (col1[1] - col2[1]);
break;
case GIMP_SELECT_CRITERION_V:
max = fabs (col1[2] - col2[2]);
break;
}
}
if (antialias && threshold > 0.0)
{
gfloat aa = 1.5 - (max / threshold);
if (aa <= 0.0)
return 0.0;
else if (aa < 0.5)
return aa * 2.0;
else
return 1.0;
}
else
{
if (max > threshold)
return 0.0;
else
return 1.0;
}
}