Нелинейная цветовая интерполяция? - PullRequest
8 голосов
/ 10 июня 2010

Если у меня есть прямая линия, которая изменяется от 0 до 1, то у меня есть colorA (255,0,0) на 0 на линии, затем на 0,3 у меня есть colorB (20,160,0), затем на 1 на линии У меня есть цвет С (0,0,0). Как я могу найти цвет на 0,7?

Спасибо

Ответы [ 5 ]

30 голосов
/ 13 июня 2010

[Разработка моих комментариев к Патрику - все вышло немного из-под контроля!]

Это действительно интересный вопрос, и одна из причин, по которой он интересен, заключается в том, чтонет «правильного» ответа.Представление цвета и интерполяция данных могут выполняться разными способами.Вы должны адаптировать подход к вашей проблемной области.Так как нам не дали много априорной информации об этой области, мы можем исследовать только некоторые из возможностей.

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

Экскурсия в интерполяцию

Допустим, у нас есть несколько точек данных, таких как:

Three points we might want to fit a function to

Мы хотим найти значение y на этом графике в точках вдоль оси x, отличных от тех, для которых у нас есть известные значения.То есть мы ищем функцию

y = f(x)

, которая проходит через эти точки.

Очевидно, что есть много разных способов присоединиться кточек.Мы могли бы просто связать их с отрезками прямой линии.Или нам может потребоваться плавная кривая, и эта кривая может быть простой или произвольно сложной:

Some possible interpolations

Здесь легко понять, откуда берутся красные линии - мыпросто рисуя прямые линии от одной известной точки к другой.Зеленая линия также выглядит разумной, хотя мы добавляем некоторые предположения о том, как должна вести себя кривая.Синюю линию, с другой стороны, было бы трудно оправдать только на основе точек данных, но может * быть обстоятельствами, когда у нас есть причины для моделирования системы с использованием такой формы - как мыпосмотрим чуть позже.

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

Хорошо, так что картина хороша и все, но как мы генерируем эти строки?

Нам нужно найти f(x), что даст нам желаемое y.Существует много различных функций, которые мы могли бы использовать в зависимости от обстоятельств, но наиболее распространенным является использование полиномиальной функции, например:

f(x) = a0 + a1 * x + a2 * x * x + a3 * x * x * x + ....

Теперь, учитывая N различных точек данных, это всегдаможно найти идеально подходящую кривую, используя многочлен степени N-1 - прямую линию для двух точек, параболу для трех, кубическую для четырех и т. д., но если данные не являются необычайно хорошими, кривая стремится ксойти с ума по мере повышения степени.Поэтому, если нет веских оснований полагать, что поведение данных хорошо моделируется полиномами более высокой степени, вместо этого обычно вместо этого интерполируют данные кусочно , подбирая отдельный сегмент кривой между каждой последовательной парой точек.

Как правило, каждый сегмент моделируется как полином степени 1 или 3 .Первый - это просто прямая линия, и он используется потому, что он действительно прост и не требует никакой информации, кроме самих двух точек данных.Последний является кубическим сплайном и используется, потому что это самый простой полином, который даст вам плавный переход через каждую точку.Однако его вычисление немного сложнее и требует двух дополнительных фрагментов информации для каждого сегмента (в зависимости от того, какая именно часть используется, зависит от используемой вами формы сплайна).

Линейная интерполяция достаточно проста, чтобы войти в одну строку кода.Если наша первая точка данных равна (x1, y1), а наша вторая - (x2, y2), то линейно-интерполированное y для любого промежуточного звена x равно:

y = y1 + (x - x1) * (y2 - y1) / (x2 - x1)

(вариации этого появляются в некоторыхдругие ответы.)

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

Вернуться к вопросу

Имея все это под нашими поясами, давайте рассмотрим проблему, как описано:

A grey bar with three known colour points

У нас есть линия (показана здесь серым цветом), цвет которой известен только в трех точках, и нас просят вычислить, какой цвет будет в другой точке.Поскольку у нас нет никаких знаний о том, как распределяются цвета, мы должны сделать некоторые предположения.

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

Random stripes

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

Известные цвета определены как RGB .В этом представлении каждый канал представляет собой отдельное скалярное значение, и мы можем рассматривать его как полностью независимое от других.Таким образом, один вполне разумный подход состоит в том, чтобы сделать кусочно-линейную интерполяцию каждого канала и затем рекомбинировать результаты.

Это дает нам что-то вроде этого:

Piecewise linear interpolation in RGB

Это нормально, но некоторые аспекты результата нам могут не понравиться.Во-первых, переход от красного к зеленому проходит через довольно мутный серо-коричневый.Другая причина заключается в том, что зеленый пик на уровне 0,3 немного резкий.

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

Поскольку у нас всего три точки данных - и поскольку Ганс Пассант предложил это - возможно, мы могли бы вместо этого попытаться подобратьпарабола для моделирования всей кривой на каждом канале?Это правда, у нас нет никаких оснований думать, что это хорошая модель, но не мешало бы попробовать:

Quadratic interpolation in RGB

Различиямежду этим градиентом и последним поучительны.Квадратик сгладил все, но также резко упал.Помните, что зеленый канал начинается и заканчивается в 0. Парабола симметрична, поэтому ее максимум должен быть посередине.Единственный способ, которым он может соответствовать зеленому росту к 0,3, - это продолжать расти до 0,5.(Существует аналогичный эффект в красном канале, но он менее очевиден, потому что в этом случае это недолгое и значение ограничено до 0.)

Есть ли у нас какие-либо доказательства того, что такая форма действительно присутствует внаша цветная линия?Нет: мы явно представили это через наш выбор модели.Это не делает его недействительным - у нас могут быть веские причины для того, чтобы это работало таким образом - еще раз, это вопрос выбора.

Но как насчет ВПГ?

Пока что мыМы придерживались оригинального цветового пространства RGB, но, как поспешили указать на это разные люди, это может быть далеко не идеальным вариантом для интерполяции: цвет и интенсивность связаны в RGB, поэтому интерполяция между двумя разными цветами полной интенсивности обычно приводит вас к некоторымсерое низкоинтенсивное среднее.

В HSV представление, цвет и интенсивность находятся в разных измерениях, поэтому у нас нет этой проблемы. Почему бы не преобразовать и не выполнить интерполяцию в этом пространстве?

Это немедленно вызывает трудности - или, во всяком случае, другое решение. Отображение между HSV и RGB не является биективным ; в частности, black , который является одной из наших трех точек данных, является единственной точкой в ​​пространстве RGB, но занимает всю плоскость в HSV. Мы не можем интерполировать между точкой и плоскостью, поэтому нам нужно выбрать одну конкретную черную точку HSV, чтобы перейти к ней.

Это основа оригинального решения Патрика, в котором H и S специально выбраны, чтобы сделать весь градиент цвета линейным. Результат выглядит примерно так:

Patrick's linear inter/extrapolation in HSV

Это выглядит намного красивее и красочнее, чем предыдущие попытки, но есть несколько вещей, с которыми мы могли бы поспорить.

Одна важная проблема заключается в том, что в случае V, где у нас все еще есть определенные данные во всех трех точках, эти данные на самом деле не являются линейными, и поэтому линейное приближение является лишь приближенным. Это означает, что значение, которое мы видим здесь в 0,3, не совсем то, что должно быть.

Еще один, на мой взгляд, более крупный спор: откуда весь этот синий цвет? Все три из наших известных точек данных RGB имеют B = 0. Кажется немного странным внезапно представить целую кучу синего цвета, для которого у нас, похоже, нет никаких доказательств. Проверьте этот график компонентов RGB в интерполяции Патрика HSV:

Graph of the RGB components in Patrick's version

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

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

Так это единственный способ сделать это в HSV? Конечно, нет. всегда больше вариантов.

Например, вместо того, чтобы выбирать значения H и S на уровне 1,0 для максимизации линейности, как насчет того, чтобы вместо этого выбрать их, чтобы минимизировать изменения в кусочно линейной интерполяции? Как это бывает, для S две стратегии совпадают: это 100 в обеих точках, поэтому мы также делаем 100 в конце. Два сегмента коллинеарны. Для H, тем не менее, мы просто оставляем это во втором сегменте. Это дает такой результат:

Piecewise linear interpolation in HSV

Это не так мило, как предыдущий, но мне кажется немного более правдоподобным. Еще раз, однако, это в значительной степени эстетическое суждение. Это не делает этот «правильный» ответ более, чем это делает ответ Патрика или любого другого «неправильным». Как я уже сказал в самом начале, «правильного» ответа нет. Все дело в том, чтобы делать выбор в соответствии с вашими потребностями в конкретной проблеме - и осознавать, что вы сделали этот выбор и как они влияют на ваш результат.

7 голосов
/ 10 июня 2010

Попробуйте преобразовать это в другое цветовое представление, например. HSV (см. http://en.wikipedia.org/wiki/HSL_and_HSV).

  • Цвет A имеет оттенок 0, насыщенность 1 и значение 1.
  • Цвет C имеет оттенок?, Насыщенность? и значение 0.

? означает, что это на самом деле не имеет значения (поскольку цвет C просто черный).

Теперь также преобразуйте цвет B в HSV (не могу этого сделать из моей головы, извините), затем выберите хорошие значения для Оттенка и Насыщенности цвета C, чтобы оттенок, значение и насыщенность находились на одной строке в пространство HSV. Затем вычтите цвет на 0,7 от него.

РЕДАКТИРОВАТЬ: Используя калькулятор RGB-HSV в http://www.csgnetwork.com/csgcolorsel4.html Я рассчитал следующее:

  • Цвет A: H: 0, V: 100, S: 100
  • Цвет B: H: 113, V: 100, S: 63

Теперь мы вычисляем H и V цвета C следующим образом:

  • Н: 113 / 0,3 = 376,6
  • V: 100 (так как A и B имеют V или 100)

Это дает нам цвет 0,7:

  • Н = 376,66 * 0,7 = 263,66
  • V = 100
  • S = где-то около 30

К сожалению, это не совсем подходит для насыщения, но если вы интерполируете таким образом, вы получите то, что очень близко к тому, что вы хотите.

4 голосов
/ 10 июня 2010

В реферате вы запрашиваете значение f (0,7), учитывая, что f (0,0) = (255, 0, 0), f (0,3) = (20, 160, 0) и f (1,0) = (0, 0, 0).Как уже говорилось, это не является четко определенным, поскольку f может быть любой из бесконечного числа функций.

Некоторые возможные варианты для f:

  1. Серия отрезков вПространство RGB
  2. Серия отрезков после преобразования в пространство HSV или HSL
  3. Кубический сплайн в пространстве RGB.
  4. Кубический сплайн в пространстве HSV / L.

Но конкретный выбор того, как следует определить f, должен зависеть от того, что вы делаете, или от того, как определены цветовые остановки для вашего приложения.

3 голосов
/ 10 июня 2010

Правильный способ интерполяции RGB - сначала исправить гамму, сделав линейные цветовые компоненты, выполнить интерполяцию, а затем преобразовать обратно. В противном случае вы получите ошибки из-за нелинейных значений. См. http://www.4p8.com/eric.brasseur/gamma.html для получения дополнительной информации (хотя статья о масштабировании, масштабирование обычно выполняется путем интерполяции значений пикселей).

Если у вас есть значения sRGB, вы можете конвертировать между линейным и нелинейным, используя формулы здесь: http://en.wikipedia.org/wiki/SRGB (используйте формулы Csrgb и Clinear)

С другой стороны, ошибки невелики, поэтому они зависят от вашего приложения.

Для самой интерполяции достаточно простой линейной интерполяции (как уже отмечали другие). См. http://en.wikipedia.org/wiki/Linear_interpolation для уточнения.

0 голосов
/ 10 июня 2010

похоже, что вы пытаетесь интерполировать в неправильном цветовом пространстве. Сначала конвертируйте в HSV, затем ваши цвета станут

RGB -> HSV
255,0,0 -> 0, 255,255
20,160,0 -> 80,255,160
0,0,0 -> 0,255,0

так что все еще сбивает с толку, но по крайней мере мы можем видеть, что значение V интерполируется до нуля, значение H сбивает с толку, но после понимания того, что HSV моделируется как цилиндр, мы можем видеть, что оно на самом деле просто интерполирует от 0 до 256 мод 256, поэтому он просто возвращается к нулю.

так что ваше уравнение будет (в ВПГ)

nH = frac*256 mod 256
nS = 255
nV =  (1-frac)*255

Таким образом, если вы замените 0,7 на ГРП, вы получите правильный расчет в координатах HSV, если вам нужно вернуться к RGB, вы должны увидеть, предоставляют ли ваши библиотеки такую ​​функцию (я знаю, класс Java поддерживает это), но если Вы не можете просто взять код из здесь . это показывает, как сделать преобразования цветового пространства в C.

Удачи

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...